1 #include "ui.h"
2
3 #include "flist.h"
4 #include "inline_video.h"
5 #include "macros.h"
6 #include "messages.h"
7 #include "settings.h"
8
9 #include "layout/background.h"
10 #include "layout/create.h"
11 #include "layout/friend.h"
12 #include "layout/group.h"
13 #include "layout/notify.h"
14 #include "layout/settings.h"
15 #include "layout/sidebar.h"
16
17 #include "native/image.h"
18 #include "native/ui.h"
19
20 #include "ui/button.h"
21 #include "ui/contextmenu.h"
22 #include "ui/draw.h"
23 #include "ui/dropdown.h"
24 #include "ui/edit.h"
25 #include "ui/panel.h"
26 #include "ui/scrollable.h"
27 #include "ui/switch.h"
28 #include "ui/text.h"
29 #include "ui/tooltip.h"
30
31 struct utox_mouse mouse;
32
33 uint8_t cursor;
34 bool mdown;
35
36 char search_data[1024]; // TODO this is NOT where this belongs
37
38 double ui_scale;
39
40 /* These remain for legacy reasons, PANEL_MAIN calls these by default when not given it's own function to call */
background_draw(PANEL * UNUSED (p),int UNUSED (x),int UNUSED (y),int UNUSED (width),int UNUSED (height))41 static void background_draw(PANEL *UNUSED(p), int UNUSED(x), int UNUSED(y), int UNUSED(width), int UNUSED(height)) {
42 return;
43 }
44
background_mmove(PANEL * UNUSED (p),int UNUSED (x),int UNUSED (y),int UNUSED (width),int UNUSED (height),int UNUSED (mx),int UNUSED (my),int UNUSED (dx),int UNUSED (dy))45 static bool background_mmove(PANEL *UNUSED(p), int UNUSED(x), int UNUSED(y), int UNUSED(width), int UNUSED(height),
46 int UNUSED(mx), int UNUSED(my), int UNUSED(dx), int UNUSED(dy)) {
47 return false;
48 }
49
background_mdown(PANEL * UNUSED (p))50 static bool background_mdown(PANEL *UNUSED(p)) {
51 return false;
52 }
53
background_mright(PANEL * UNUSED (p))54 static bool background_mright(PANEL *UNUSED(p)) {
55 return false;
56 }
57
background_mwheel(PANEL * UNUSED (p),int UNUSED (height),double UNUSED (d),bool UNUSED (smooth))58 static bool background_mwheel(PANEL *UNUSED(p), int UNUSED(height), double UNUSED(d), bool UNUSED(smooth)) {
59 return false;
60 }
61
background_mup(PANEL * UNUSED (p))62 static bool background_mup(PANEL *UNUSED(p)) {
63 return false;
64 }
65
background_mleave(PANEL * UNUSED (p))66 static bool background_mleave(PANEL *UNUSED(p)) {
67 return false;
68 }
69
70 /***** MAYBE_I18NAL_STRING helpers start *****/
71
maybe_i18nal_string_set_plain(MAYBE_I18NAL_STRING * mis,char * str,uint16_t length)72 void maybe_i18nal_string_set_plain(MAYBE_I18NAL_STRING *mis, char *str, uint16_t length) {
73 mis->i18nal = UI_STRING_ID_INVALID;
74 mis->plain.length = length;
75 mis->plain.str = str;
76 }
77
maybe_i18nal_string_set_i18nal(MAYBE_I18NAL_STRING * mis,UTOX_I18N_STR string_id)78 void maybe_i18nal_string_set_i18nal(MAYBE_I18NAL_STRING *mis, UTOX_I18N_STR string_id) {
79 mis->plain.str = NULL;
80 mis->plain.length = 0;
81 mis->i18nal = string_id;
82 }
83
maybe_i18nal_string_get(MAYBE_I18NAL_STRING * mis)84 STRING *maybe_i18nal_string_get(MAYBE_I18NAL_STRING *mis) {
85 if (mis->plain.str) {
86 return &mis->plain;
87 }
88
89 return SPTRFORLANG(settings.language, mis->i18nal);
90 }
91
maybe_i18nal_string_is_valid(MAYBE_I18NAL_STRING * mis)92 bool maybe_i18nal_string_is_valid(MAYBE_I18NAL_STRING *mis) {
93 return (mis->plain.str || ((UI_STRING_ID_INVALID != mis->i18nal) && (mis->i18nal < NUM_STRS)));
94 }
95
96 /***********************************************************************
97 * *
98 * Panel layout size set functions. *
99 * *
100 **********************************************************************/
sidepanel_USERBADGE(void)101 static void sidepanel_USERBADGE(void) {
102 // Converting DEFINES to magic because this will be moved to layout/
103 // and will then get a different format/selection
104 }
105
sidepanel_FLIST(void)106 static void sidepanel_FLIST(void) {
107 scrollbar_flist.panel.y = 0;
108 // scrollbar_flist.panel.width = 230; // TODO remove?
109 scrollbar_flist.panel.height = -1;
110
111 panel_flist.x = 0;
112 panel_flist.y = 70;
113 panel_flist.width = 230; // TODO remove?
114 panel_flist.height = ROSTER_BOTTOM;
115
116
117 button_add_new_contact.panel.disabled = true;
118 }
119
120
settings_PROFILE(void)121 static void settings_PROFILE(void) {
122 panel_settings_profile.y = 32;
123
124 }
125
settings_UI(void)126 static void settings_UI(void) {
127 panel_settings_ui.y = 32;
128
129 }
130
settings_AV(void)131 static void settings_AV(void) {
132 panel_settings_av.y = 32;
133
134
135 #ifndef AUDIO_FILTERING
136 const uint16_t start_draw_y = 30;
137 const uint16_t preview_button_pos_y = 245;
138 #else
139 const uint16_t start_draw_y = 60;
140 const uint16_t preview_button_pos_y = 275;
141 CREATE_SWITCH(audio_filtering, 10, 40, _BM_SWITCH_WIDTH, _BM_SWITCH_HEIGHT);
142 #endif
143
144
145 const uint16_t draw_y_vect = 30;
146 CREATE_DROPDOWN(audio_in, 10, (start_draw_y + draw_y_vect + 5), 24, 360);
147 CREATE_DROPDOWN(audio_out, 10, (start_draw_y + draw_y_vect + 57), 24, 360);
148 CREATE_EDIT(video_fps, 10, (start_draw_y + draw_y_vect + 110), 360, 24);
149 CREATE_DROPDOWN(video, 10, (start_draw_y + draw_y_vect + 162), 24, 360);
150
151 CREATE_BUTTON(callpreview, 10, (preview_button_pos_y + 35), _BM_LBUTTON_WIDTH, _BM_LBUTTON_HEIGHT);
152 CREATE_BUTTON(videopreview, 70, (preview_button_pos_y + 35), _BM_LBUTTON_WIDTH, _BM_LBUTTON_HEIGHT);
153 }
154
settings_NOTIFY(void)155 static void settings_NOTIFY(void) {
156 panel_settings_notifications.y = 32;
157
158 }
159
settings_ADV(void)160 static void settings_ADV(void) {
161 panel_settings_adv.y = 32;
162
163
164 const int show_nospam_x = 30 + UN_SCALE(MAX(UTOX_STR_WIDTH(SHOW_UI_PASSWORD), UTOX_STR_WIDTH(HIDE_UI_PASSWORD)));
165 CREATE_BUTTON(show_nospam, show_nospam_x, 177, _BM_SBUTTON_WIDTH, _BM_SBUTTON_HEIGHT);
166
167
168 const int revert_nospam_x = 30 + UN_SCALE(UTOX_STR_WIDTH(RANDOMIZE_NOSPAM));
169 CREATE_BUTTON(revert_nospam, revert_nospam_x, 265, _BM_SBUTTON_WIDTH, _BM_SBUTTON_HEIGHT);
170
171 }
172
ui_set_scale(uint8_t scale)173 void ui_set_scale(uint8_t scale) {
174 if (scale >= 5 && scale <= 25) {
175 ui_scale = scale;
176 } else if (scale != 0) {
177 return ui_set_scale(10);
178 }
179 }
180
ui_rescale(uint8_t scale)181 void ui_rescale(uint8_t scale) {
182 ui_set_scale(scale);
183
184 flist_re_scale();
185 setscale_fonts();
186 setfont(FONT_SELF_NAME);
187
188 /* DEFAULT positions */
189
190 panel_main.y = 0;
191
192 scrollbar_settings.panel.y = 32; /* TODO magic numbers are bad */
193 scrollbar_settings.content_height = 300; /* TODO magic numbers are bad */
194
195 panel_settings_master.y = 0;
196 panel_settings_devices.y = 32;
197 panel_settings_adv.y = 32;
198
199 scrollbar_friend.panel.y = MAIN_TOP;
200 scrollbar_friend.panel.height = CHAT_BOX_TOP;
201 messages_friend.y = MAIN_TOP;
202 messages_friend.height = CHAT_BOX_TOP - 10;
203 messages_friend.width = -SCROLL_WIDTH;
204
205 scrollbar_group.panel.y = MAIN_TOP;
206 scrollbar_group.panel.height = CHAT_BOX_TOP;
207 messages_group.y = MAIN_TOP;
208 messages_group.height = CHAT_BOX_TOP;
209 messages_group.width = -SCROLL_WIDTH;
210
211 setfont(FONT_SELF_NAME);
212
213 sidepanel_USERBADGE();
214 sidepanel_FLIST();
215
216 settings_PROFILE();
217 settings_UI();
218 settings_AV();
219 settings_NOTIFY();
220 settings_ADV();
221
222 // FIXME for testing, remove
223 CREATE_BUTTON(notify_create, 2, 2, BM_SBUTTON_WIDTH, BM_SBUTTON_HEIGHT);
224 CREATE_BUTTON(notify_one, 0, -50, 40, 50);
225 CREATE_BUTTON(notify_two, 200, -50, 40, 50);
226 CREATE_BUTTON(notify_three, -40, -50, 40, 50);
227
228 CREATE_BUTTON(move_notify, -40, -40, 40, 40);
229
230
231 /* Setting pages */
232 uint32_t settings_x = 4;
233 CREATE_BUTTON(settings_sub_profile, settings_x, 0, 12, 28);
234 settings_x += 20 + UN_SCALE(UTOX_STR_WIDTH(PROFILE_BUTTON));
235
236 #ifdef ENABLE_MULTIDEVICE
237 CREATE_BUTTON(settings_sub_devices, settings_x, 0, 12, 28);
238 settings_x += 20 + UN_SCALE(UTOX_STR_WIDTH(DEVICES_BUTTON));
239 #endif
240
241 CREATE_BUTTON(settings_sub_ui, settings_x, 0, 12, 28);
242 settings_x += 20 + UN_SCALE(UTOX_STR_WIDTH(USER_INTERFACE_BUTTON));
243
244 CREATE_BUTTON(settings_sub_av, settings_x, 0, 12, 28);
245 settings_x += 20 + UN_SCALE(UTOX_STR_WIDTH(AUDIO_VIDEO_BUTTON));
246
247 CREATE_BUTTON(settings_sub_notifications, settings_x, 0, 12, 28);
248 settings_x += 20 + UN_SCALE(UTOX_STR_WIDTH(NOTIFICATIONS_BUTTON));
249
250 CREATE_BUTTON(settings_sub_adv, settings_x, 0, 12, 28);
251
252
253 /* Devices */
254 CREATE_BUTTON(add_new_device_to_self, -10 - BM_SBUTTON_WIDTH, 28, BM_SBUTTON_WIDTH, BM_SBUTTON_HEIGHT);
255
256 CREATE_EDIT(add_new_device_to_self, 10, 27, 0 - UTOX_STR_WIDTH(ADD) - BM_SBUTTON_WIDTH, 24);
257
258
259
260 setfont(FONT_TEXT);
261
262
263 CREATE_EDIT(chat_msg_group, 6, -46, -10 - BM_CHAT_SEND_WIDTH, 40);
264
265
266 setscale();
267 }
268
269 /* Use the preprocessor to build function prototypes for all user interactions
270 * These are functions that are (must be) defined elsewhere. The preprocessor in this case creates the prototypes that
271 * will then be used by panel_draw_core to call the correct function
272 */
273 #define MAKE_FUNC(ret, x, ...) \
274 static ret (*x##func[])(void *p, ##__VA_ARGS__) = { \
275 (void *)background_##x, (void *)messages_##x, (void *)inline_video_##x, (void *)flist_##x, (void *)button_##x, \
276 (void *)switch_##x, (void *)dropdown_##x, (void *)edit_##x, (void *)scroll_##x, \
277 };
278
279 MAKE_FUNC(void, draw, int x, int y, int width, int height);
280 MAKE_FUNC(bool, mmove, int x, int y, int width, int height, int mx, int my, int dx, int dy);
281 MAKE_FUNC(bool, mdown);
282 MAKE_FUNC(bool, mright);
283 MAKE_FUNC(bool, mwheel, int height, double d);
284 MAKE_FUNC(bool, mup);
285 MAKE_FUNC(bool, mleave);
286
287 #undef MAKE_FUNC
288
289 /* Use the preprocessor to add code to adjust the x,y cords for panels or sub panels.
290 * If neg value place x/y from the right/bottom of panel.
291 *
292 * change the relative
293 *
294 * if w/h <0 use parent panel width (maybe?) */
295 #define FIX_XY_CORDS_FOR_SUBPANELS() \
296 { \
297 int relx = (p->x < 0) ? width + SCALE(p->x) : SCALE(p->x); \
298 int rely = (p->y < 0) ? height + SCALE(p->y) : SCALE(p->y); \
299 x += relx; \
300 y += rely; \
301 width = (p->width <= 0) ? width + SCALE(p->width) - relx : SCALE(p->width); \
302 height = (p->height <= 0) ? height + SCALE(p->height) - rely : SCALE(p->height); \
303 }
304
panel_update(PANEL * p,int x,int y,int width,int height)305 static void panel_update(PANEL *p, int x, int y, int width, int height) {
306 FIX_XY_CORDS_FOR_SUBPANELS();
307
308 switch (p->type) {
309 case PANEL_NONE: {
310 if (p == &panel_settings_devices) {
311 #ifdef ENABLE_MULTIDEVICE
312 devices_update_ui();
313 #endif
314 }
315 break;
316 }
317
318 case PANEL_MESSAGES: {
319 if (p->object) {
320 MESSAGES *m = p->object;
321 m->width = width;
322 messages_updateheight(m, width);
323 }
324 break;
325 }
326
327 default: {
328 break;
329 }
330 }
331
332 PANEL **pp = p->child;
333 if (pp) {
334 if (p->update) {
335 p->update(width, height, ui_scale);
336 }
337
338 PANEL *subp;
339 while ((subp = *pp++)) {
340 panel_update(subp, x, y, width, height);
341 }
342 }
343 }
344
draw_avatar_image(NATIVE_IMAGE * image,int x,int y,uint32_t width,uint32_t height,uint32_t targetwidth,uint32_t targetheight)345 void draw_avatar_image(NATIVE_IMAGE *image, int x, int y, uint32_t width, uint32_t height, uint32_t targetwidth,
346 uint32_t targetheight)
347 {
348 /* get smallest of width or height */
349 const double scale = (width > height) ? (double)targetheight / height : (double)targetwidth / width;
350
351 image_set_scale(image, scale);
352 image_set_filter(image, FILTER_BILINEAR);
353
354 /* set position to show the middle of the image in the center */
355 const int xpos = (int)((double)width * scale / 2 - (double)targetwidth / 2);
356 const int ypos = (int)((double)height * scale / 2 - (double)targetheight / 2);
357
358 draw_image(image, x, y, targetwidth, targetheight, xpos, ypos);
359
360 image_set_scale(image, 1.0);
361 image_set_filter(image, FILTER_NEAREST);
362 }
363
ui_size(int width,int height)364 void ui_size(int width, int height) {
365 panel_update(&panel_root, 0, 0, width, height);
366 tooltip_reset();
367
368 panel_side_bar.disabled = false;
369 panel_main.x = panel_flist.width;
370
371 if (settings.magic_flist_enabled) {
372 if (width <= panel_flist.width * 2 || height > width) {
373 panel_side_bar.disabled = true;
374 panel_main.x = 0;
375 }
376 }
377 }
378
ui_mouseleave(void)379 void ui_mouseleave(void) {
380 panel_mleave(&panel_root);
381 tooltip_reset();
382 redraw();
383 }
384
panel_draw_core(PANEL * p,int x,int y,int width,int height)385 static void panel_draw_core(PANEL *p, int x, int y, int width, int height) {
386 FIX_XY_CORDS_FOR_SUBPANELS();
387
388 if (p->content_scroll) {
389 pushclip(x, y, width, height);
390 y -= scroll_gety(p->content_scroll, height);
391 }
392
393 if (p->type) {
394 drawfunc[p->type - 1](p, x, y, width, height);
395 } else {
396 if (p->drawfunc) {
397 p->drawfunc(x, y, width, height);
398 }
399 }
400
401 PANEL **pp = p->child;
402 if (pp) {
403 PANEL *subp;
404 while ((subp = *pp++)) {
405 if (!subp->disabled) {
406 panel_draw_core(subp, x, y, width, height);
407 }
408 }
409 }
410
411 if (p->content_scroll) {
412 popclip();
413 }
414 }
415
panel_draw(PANEL * p,int x,int y,int width,int height)416 void panel_draw(PANEL *p, int x, int y, int width, int height) {
417 FIX_XY_CORDS_FOR_SUBPANELS();
418
419 panel_draw_core(p, x, y, width, height);
420
421 // popclip();
422
423 dropdown_drawactive();
424 contextmenu_draw();
425 tooltip_draw();
426
427 enddraw(x, y, width, height);
428 }
429
panel_mmove(PANEL * p,int x,int y,int width,int height,int mx,int my,int dx,int dy)430 bool panel_mmove(PANEL *p, int x, int y, int width, int height, int mx, int my, int dx, int dy) {
431 if (p == &panel_root) {
432 mouse.x = mx;
433 mouse.y = my;
434 }
435
436 mx -= (p->x < 0) ? width + SCALE(p->x) : SCALE(p->x);
437 my -= (p->y < 0) ? height + SCALE(p->y) : SCALE(p->y);
438 FIX_XY_CORDS_FOR_SUBPANELS();
439
440 int mmy = my;
441
442 if (p->content_scroll) {
443 const int scroll_y = scroll_gety(p->content_scroll, height);
444 if (my < 0) {
445 mmy = -1;
446 } else if (my >= height) {
447 mmy = 1024 * 1024 * 1024; // large value
448 } else {
449 mmy = my + scroll_y;
450 }
451 y -= scroll_y;
452 my += scroll_y;
453 }
454
455 bool draw = p->type ? mmovefunc[p->type - 1](p, x, y, width, height, mx, mmy, dx, dy) : false;
456 // Has to be called before children mmove
457 if (p == &panel_root) {
458 draw |= tooltip_mmove();
459 }
460 PANEL **pp = p->child;
461 if (pp) {
462 PANEL *subp;
463 while ((subp = *pp++)) {
464 if (!subp->disabled) {
465 draw |= panel_mmove(subp, x, y, width, height, mx, my, dx, dy);
466 }
467 }
468 }
469
470 if (p == &panel_root) {
471 draw |= contextmenu_mmove(mx, my, dx, dy);
472 if (draw) {
473 redraw();
474 }
475 }
476
477 return draw;
478 }
479
panel_mdown_sub(PANEL * p)480 static bool panel_mdown_sub(PANEL *p) {
481 if (p->type && mdownfunc[p->type - 1](p)) {
482 return true;
483 }
484
485 PANEL **pp = p->child;
486 if (pp) {
487 PANEL *subp;
488 while ((subp = *pp++)) {
489 if (!subp->disabled) {
490 if (panel_mdown_sub(subp)) {
491 return true;
492 }
493 }
494 }
495 }
496
497 return false;
498 }
499
panel_mdown(PANEL * p)500 void panel_mdown(PANEL *p) {
501 if (contextmenu_mdown() || tooltip_mdown()) {
502 redraw();
503 return;
504 }
505
506 bool draw = edit_active();
507
508 PANEL **pp = p->child;
509
510 if (pp) {
511 PANEL *subp;
512 while ((subp = *pp++)) {
513 if (!subp->disabled) {
514 draw |= panel_mdown_sub(subp);
515 }
516 }
517 }
518
519 if (draw) {
520 redraw();
521 }
522 }
523
panel_dclick(PANEL * p,bool triclick)524 bool panel_dclick(PANEL *p, bool triclick) {
525 bool draw = false;
526 if (p->type == PANEL_EDIT) {
527 draw = edit_dclick((EDIT *)p, triclick);
528 } else if (p->type == PANEL_MESSAGES) {
529 draw = messages_dclick(p, triclick);
530 }
531
532 PANEL **pp = p->child;
533 if (pp) {
534 PANEL *subp;
535 while ((subp = *pp++)) {
536 if (!subp->disabled) {
537 draw = panel_dclick(subp, triclick);
538 if (draw) {
539 break;
540 }
541 }
542 }
543 }
544
545 if (draw && p == &panel_root) {
546 redraw();
547 }
548
549 return draw;
550 }
551
panel_mright(PANEL * p)552 bool panel_mright(PANEL *p) {
553 bool draw = p->type ? mrightfunc[p->type - 1](p) : false;
554 PANEL **pp = p->child;
555 if (pp) {
556 PANEL *subp;
557 while ((subp = *pp++)) {
558 if (!subp->disabled) {
559 draw |= panel_mright(subp);
560 }
561 }
562 }
563
564 if (draw && p == &panel_root) {
565 redraw();
566 }
567
568 return draw;
569 }
570
panel_mwheel(PANEL * p,int x,int y,int width,int height,double d,bool smooth)571 bool panel_mwheel(PANEL *p, int x, int y, int width, int height, double d, bool smooth) {
572 FIX_XY_CORDS_FOR_SUBPANELS();
573
574 bool draw = p->type ? mwheelfunc[p->type - 1](p, height, d) : false;
575 PANEL **pp = p->child;
576 if (pp) {
577 PANEL *subp;
578 while ((subp = *pp++)) {
579 if (!subp->disabled) {
580 draw |= panel_mwheel(subp, x, y, width, height, d, smooth);
581 }
582 }
583 }
584
585 if (draw && p == &panel_root) {
586 redraw();
587 }
588
589 return draw;
590 }
591
panel_mup(PANEL * p)592 bool panel_mup(PANEL *p) {
593 if (p == &panel_root && contextmenu_mup()) {
594 tooltip_mup();
595 redraw();
596 return true;
597 }
598
599 bool draw = p->type ? mupfunc[p->type - 1](p) : false;
600 PANEL **pp = p->child;
601 if (pp) {
602 PANEL *subp;
603 while ((subp = *pp++)) {
604 if (!subp->disabled) {
605 draw |= panel_mup(subp);
606 }
607 }
608 }
609
610 if (p == &panel_root) {
611 tooltip_mup();
612 if (draw) {
613 redraw();
614 }
615 }
616
617 return draw;
618 }
619
panel_mleave(PANEL * p)620 bool panel_mleave(PANEL *p) {
621 bool draw = p->type ? mleavefunc[p->type - 1](p) : false;
622 PANEL **pp = p->child;
623 if (pp) {
624 PANEL *subp;
625 while ((subp = *pp++)) {
626 if (!subp->disabled) {
627 draw |= panel_mleave(subp);
628 }
629 }
630 }
631
632 if (p == &panel_root) {
633 draw |= contextmenu_mleave();
634 if (draw) {
635 redraw();
636 }
637 }
638
639 return draw;
640 }
641