showdialog.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2018 K. Lange
  5. *
  6. * showdialog - show a window with a dialog prompt with buttons
  7. */
  8. #include <toaru/yutani.h>
  9. #include <toaru/graphics.h>
  10. #include <toaru/decorations.h>
  11. #include <toaru/sdf.h>
  12. #include <toaru/menu.h>
  13. #include <toaru/button.h>
  14. #include <sys/utsname.h>
  15. #define BUTTON_HEIGHT 28
  16. #define BUTTON_WIDTH 86
  17. #define BUTTON_PADDING 14
  18. static yutani_t * yctx;
  19. static yutani_window_t * window = NULL;
  20. static gfx_context_t * ctx = NULL;
  21. static sprite_t logo;
  22. static int32_t width = 600;
  23. static int32_t height = 150;
  24. static char * icon_path;
  25. static char * title_str;
  26. static char * copyright_str[20] = {NULL};
  27. static void draw_string(int y, const char * string, int font, uint32_t color) {
  28. struct decor_bounds bounds;
  29. decor_get_bounds(window, &bounds);
  30. draw_sdf_string(ctx, bounds.left_width + 80, bounds.top_height + 30 + y, string, 16, color, font);
  31. }
  32. struct TTKButton _ok = {0};
  33. struct TTKButton _cancel = {0};
  34. static void redraw(void) {
  35. struct decor_bounds bounds;
  36. decor_get_bounds(window, &bounds);
  37. draw_fill(ctx, rgb(204,204,204));
  38. draw_sprite(ctx, &logo, bounds.left_width + 20, bounds.top_height + 20);
  39. int offset = 0;
  40. for (char ** copy_str = copyright_str; *copy_str; ++copy_str) {
  41. if (**copy_str == '-') {
  42. offset += 10;
  43. } else if (**copy_str == '%') {
  44. draw_string(offset, *copy_str+1, SDF_FONT_THIN, rgb(0,0,255));
  45. offset += 20;
  46. } else {
  47. draw_string(offset, *copy_str, SDF_FONT_THIN, rgb(0,0,0));
  48. offset += 20;
  49. }
  50. }
  51. ttk_button_draw(ctx, &_ok);
  52. ttk_button_draw(ctx, &_cancel);
  53. window->decorator_flags |= DECOR_FLAG_NO_MAXIMIZE;
  54. render_decorations(window, ctx, title_str);
  55. flip(ctx);
  56. yutani_flip(yctx, window);
  57. }
  58. static void init_default(void) {
  59. title_str = "Dialog Prompt";
  60. icon_path = "/usr/share/icons/48/folder.bmp";
  61. copyright_str[0] = "This is a demonstration of a dialog box.";
  62. copyright_str[1] = "You can press \"Okay\" or \"Cancel\" or close the window.";
  63. }
  64. int in_button(struct TTKButton * button, struct yutani_msg_window_mouse_event * me) {
  65. if (me->new_y >= button->y && me->new_y < button->y + button->height) {
  66. if (me->new_x >= button->x && me->new_x < button->x + button->width) {
  67. return 1;
  68. }
  69. }
  70. return 0;
  71. }
  72. void setup_buttons(void) {
  73. struct decor_bounds bounds;
  74. decor_get_bounds(window, &bounds);
  75. _ok.title = "Okay";
  76. _ok.width = BUTTON_WIDTH;
  77. _ok.height = BUTTON_HEIGHT;
  78. _ok.x = ctx->width - bounds.right_width - BUTTON_WIDTH - BUTTON_PADDING;
  79. _ok.y = ctx->height - bounds.bottom_height - BUTTON_HEIGHT - BUTTON_PADDING;
  80. _cancel.title = "Cancel";
  81. _cancel.width = BUTTON_WIDTH;
  82. _cancel.height = BUTTON_HEIGHT;
  83. _cancel.x = ctx->width - bounds.right_width - BUTTON_WIDTH * 2 - BUTTON_PADDING * 2;
  84. _cancel.y = ctx->height - bounds.bottom_height - BUTTON_HEIGHT - BUTTON_PADDING;
  85. }
  86. void resize_finish(int w, int h) {
  87. yutani_window_resize_accept(yctx, window, w, h);
  88. reinit_graphics_yutani(ctx, window);
  89. width = w;
  90. height = h;
  91. setup_buttons();
  92. redraw();
  93. yutani_window_resize_done(yctx, window);
  94. }
  95. void set_hilight(struct TTKButton * button, int hilight) {
  96. if (!button && (_ok.hilight || _cancel.hilight)) {
  97. _ok.hilight = 0;
  98. _cancel.hilight = 0;
  99. redraw();
  100. } else if (button && (button->hilight != hilight)) {
  101. _ok.hilight = 0;
  102. _cancel.hilight = 0;
  103. button->hilight = hilight;
  104. redraw();
  105. }
  106. }
  107. int main(int argc, char * argv[]) {
  108. int req_center_x, req_center_y;
  109. yctx = yutani_init();
  110. if (!yctx) {
  111. fprintf(stderr, "%s: failed to connect to compositor\n", argv[0]);
  112. return 1;
  113. }
  114. init_decorations();
  115. struct decor_bounds bounds;
  116. decor_get_bounds(NULL, &bounds);
  117. window = yutani_window_create_flags(yctx, width + bounds.width, height + bounds.height, YUTANI_WINDOW_FLAG_DIALOG_ANIMATION);
  118. req_center_x = yctx->display_width / 2;
  119. req_center_y = yctx->display_height / 2;
  120. if (argc < 2) {
  121. init_default();
  122. } else if (argc < 4) {
  123. fprintf(stderr, "Invalid arguments.\n");
  124. return 1;
  125. } else {
  126. title_str = argv[1];
  127. icon_path = argv[2];
  128. int i = 0;
  129. char * me = argv[3], * end;
  130. do {
  131. copyright_str[i] = me;
  132. i++;
  133. end = strchr(me,'\n');
  134. if (end) {
  135. *end = '\0';
  136. me = end+1;
  137. }
  138. } while (end);
  139. if (argc > 6) {
  140. req_center_x = atoi(argv[5]);
  141. req_center_y = atoi(argv[6]);
  142. }
  143. }
  144. yutani_window_move(yctx, window, req_center_x - window->width / 2, req_center_y - window->height / 2);
  145. yutani_window_advertise_icon(yctx, window, title_str, "star");
  146. ctx = init_graphics_yutani_double_buffer(window);
  147. setup_buttons();
  148. load_sprite(&logo, icon_path);
  149. logo.alpha = ALPHA_EMBEDDED;
  150. redraw();
  151. struct TTKButton * _down_button = NULL;
  152. int playing = 1;
  153. int status = 0;
  154. while (playing) {
  155. yutani_msg_t * m = yutani_poll(yctx);
  156. while (m) {
  157. if (menu_process_event(yctx, m)) {
  158. redraw();
  159. }
  160. switch (m->type) {
  161. case YUTANI_MSG_KEY_EVENT:
  162. {
  163. struct yutani_msg_key_event * ke = (void*)m->data;
  164. if (ke->event.action == KEY_ACTION_DOWN && ke->event.keycode == '\n') {
  165. playing = 0;
  166. status = 0;
  167. } else if (ke->event.action == KEY_ACTION_DOWN && ke->event.keycode == KEY_ESCAPE) {
  168. playing = 0;
  169. status = 2;
  170. }
  171. }
  172. break;
  173. case YUTANI_MSG_WINDOW_FOCUS_CHANGE:
  174. {
  175. struct yutani_msg_window_focus_change * wf = (void*)m->data;
  176. yutani_window_t * win = hashmap_get(yctx->windows, (void*)wf->wid);
  177. if (win) {
  178. win->focused = wf->focused;
  179. redraw();
  180. }
  181. }
  182. break;
  183. case YUTANI_MSG_RESIZE_OFFER:
  184. {
  185. struct yutani_msg_window_resize * wr = (void*)m->data;
  186. resize_finish(wr->width, wr->height);
  187. }
  188. break;
  189. case YUTANI_MSG_WINDOW_MOUSE_EVENT:
  190. {
  191. struct yutani_msg_window_mouse_event * me = (void*)m->data;
  192. if (me->wid == window->wid) {
  193. int result = decor_handle_event(yctx, m);
  194. switch (result) {
  195. case DECOR_CLOSE:
  196. playing = 0;
  197. status = 2;
  198. break;
  199. case DECOR_RIGHT:
  200. /* right click in decoration, show appropriate menu */
  201. decor_show_default_menu(window, window->x + me->new_x, window->y + me->new_y);
  202. break;
  203. default:
  204. /* Other actions */
  205. break;
  206. }
  207. struct decor_bounds bounds;
  208. decor_get_bounds(window, &bounds);
  209. if (me->new_y > bounds.top_height) {
  210. if (me->command == YUTANI_MOUSE_EVENT_DOWN) {
  211. if (in_button(&_ok, me)) {
  212. set_hilight(&_ok, 2);
  213. _down_button = &_ok;
  214. } else if (in_button(&_cancel, me)) {
  215. set_hilight(&_cancel, 2);
  216. _down_button = &_cancel;
  217. }
  218. } else if (me->command == YUTANI_MOUSE_EVENT_RAISE || me->command == YUTANI_MOUSE_EVENT_CLICK) {
  219. if (_down_button) {
  220. if (in_button(_down_button, me)) {
  221. if (_down_button == &_cancel) {
  222. playing = 0;
  223. status = 1;
  224. break;
  225. } else if (_down_button == &_ok) {
  226. playing = 0;
  227. status = 0;
  228. break;
  229. }
  230. _down_button->hilight = 0;
  231. }
  232. }
  233. _down_button = NULL;
  234. }
  235. if (!me->buttons & YUTANI_MOUSE_BUTTON_LEFT) {
  236. if (in_button(&_ok, me)) {
  237. set_hilight(&_ok, 1);
  238. } else if (in_button(&_cancel, me)) {
  239. set_hilight(&_cancel, 1);
  240. } else {
  241. set_hilight(NULL,0);
  242. }
  243. } else if (_down_button) {
  244. if (in_button(_down_button, me)) {
  245. set_hilight(_down_button, 2);
  246. } else {
  247. set_hilight(NULL, 0);
  248. }
  249. }
  250. }
  251. }
  252. }
  253. break;
  254. case YUTANI_MSG_WINDOW_CLOSE:
  255. case YUTANI_MSG_SESSION_END:
  256. playing = 0;
  257. status = 2;
  258. break;
  259. default:
  260. break;
  261. }
  262. free(m);
  263. m = yutani_poll_async(yctx);
  264. }
  265. }
  266. yutani_close(yctx, window);
  267. return status;
  268. }