From 2533049e28a2a5dbb45977e075e52b31d582334e Mon Sep 17 00:00:00 2001
From: Mooffie <mooffie@gmail.com>
Date: Sun, 13 Mar 2016 21:22:25 +0200
Subject: [PATCH] Fix menu handling.
We now send mouse events to widgets in reverse Z-order, as done in common GUIs.
This makes it easy to implement "invisible" menubars correctly.
---
lib/widget/dialog.c | 49 ++++++++++++++++++++++++++++-----------
lib/widget/dialog.h | 1 +
lib/widget/menu.c | 11 +++++++--
lib/widget/mouse.c | 34 ---------------------------
lib/widget/mouse.h | 1 -
src/filemanager/midnight.c | 58 +---------------------------------------------
6 files changed, 46 insertions(+), 108 deletions(-)
diff --git a/lib/widget/dialog.c b/lib/widget/dialog.c
index 8b8f222..5855750 100644
a
|
b
|
dlg_mouse_event (WDialog * h, Gpm_Event * event) |
380 | 380 | { |
381 | 381 | Widget *wh = WIDGET (h); |
382 | 382 | |
383 | | GList *p, *first; |
| 383 | GList *p; |
384 | 384 | |
385 | 385 | /* close the dialog by mouse left click out of dialog area */ |
386 | 386 | if (mouse_close_dialog && !h->fullscreen && ((event->buttons & GPM_B_LEFT) != 0) |
… |
… |
dlg_mouse_event (WDialog * h, Gpm_Event * event) |
400 | 400 | return mou; |
401 | 401 | } |
402 | 402 | |
403 | | first = h->current; |
404 | | p = first; |
405 | | |
| 403 | /* send the event to widgets in reverse Z-order */ |
| 404 | p = g_list_last (h->widgets); |
406 | 405 | do |
407 | 406 | { |
408 | 407 | Widget *w = WIDGET (p->data); |
409 | 408 | |
410 | | p = dlg_widget_prev (h, p); |
411 | | |
412 | 409 | if ((w->options & W_DISABLED) == 0 && w->mouse_callback != NULL) |
413 | 410 | { |
414 | 411 | /* put global cursor position to the widget */ |
… |
… |
dlg_mouse_event (WDialog * h, Gpm_Event * event) |
418 | 415 | if (ret != MOU_UNHANDLED) |
419 | 416 | return ret; |
420 | 417 | } |
| 418 | |
| 419 | p = g_list_previous (p); |
421 | 420 | } |
422 | | while (p != first); |
| 421 | while (p != NULL); |
423 | 422 | |
424 | 423 | return MOU_UNHANDLED; |
425 | 424 | } |
… |
… |
dlg_select_widget (void *w) |
1090 | 1089 | |
1091 | 1090 | /* --------------------------------------------------------------------------------------------- */ |
1092 | 1091 | |
1093 | | /** |
1094 | | * Set widget at top of widget list and make it current. |
1095 | | */ |
1096 | | |
1097 | | void |
1098 | | dlg_set_top_widget (void *w) |
| 1092 | static void |
| 1093 | dlg_set_top_or_bottom_widget (void *w, gboolean set_top) |
1099 | 1094 | { |
1100 | 1095 | Widget *widget = WIDGET (w); |
1101 | 1096 | WDialog *h = widget->owner; |
… |
… |
dlg_set_top_widget (void *w) |
1111 | 1106 | |
1112 | 1107 | /* widget reordering */ |
1113 | 1108 | h->widgets = g_list_remove_link (h->widgets, l); |
1114 | | h->widgets = g_list_concat (h->widgets, l); |
| 1109 | if (set_top) |
| 1110 | h->widgets = g_list_concat (h->widgets, l); |
| 1111 | else |
| 1112 | h->widgets = g_list_concat (l, h->widgets); |
1115 | 1113 | h->current = l; |
1116 | 1114 | } |
1117 | 1115 | |
1118 | 1116 | /* --------------------------------------------------------------------------------------------- */ |
| 1117 | /** |
| 1118 | * Set widget at top of widget list and make it current. |
| 1119 | */ |
| 1120 | |
| 1121 | void |
| 1122 | dlg_set_top_widget (void *w) |
| 1123 | { |
| 1124 | dlg_set_top_or_bottom_widget (w, TRUE); |
| 1125 | } |
| 1126 | |
| 1127 | /* --------------------------------------------------------------------------------------------- */ |
| 1128 | /** |
| 1129 | * Set widget at bottom of widget list (and make it current, albeit |
| 1130 | * typically you'd want to switch to some other widget right after). |
| 1131 | */ |
| 1132 | |
| 1133 | void |
| 1134 | dlg_set_bottom_widget (void *w) |
| 1135 | { |
| 1136 | dlg_set_top_or_bottom_widget (w, FALSE); |
| 1137 | } |
| 1138 | |
| 1139 | /* --------------------------------------------------------------------------------------------- */ |
1119 | 1140 | /** Try to select previous widget in the tab order */ |
1120 | 1141 | |
1121 | 1142 | void |
diff --git a/lib/widget/dialog.h b/lib/widget/dialog.h
index 2510591..6ff23d8 100644
a
|
b
|
void dlg_stop (WDialog * h); |
170 | 170 | /* Widget selection */ |
171 | 171 | void dlg_select_widget (void *w); |
172 | 172 | void dlg_set_top_widget (void *w); |
| 173 | void dlg_set_bottom_widget (void *w); |
173 | 174 | void dlg_one_up (WDialog * h); |
174 | 175 | void dlg_one_down (WDialog * h); |
175 | 176 | gboolean dlg_focus (WDialog * h); |
diff --git a/lib/widget/menu.c b/lib/widget/menu.c
index b873d79..f542785 100644
a
|
b
|
menubar_finish (WMenuBar * menubar) |
309 | 309 | w->lines = 1; |
310 | 310 | widget_want_hotkey (w, 0); |
311 | 311 | |
| 312 | /* Move the menubar to the bottom so that widgets displayed on top of |
| 313 | * an "invisible" menubar get the first chance to respond to mouse events. */ |
| 314 | dlg_set_bottom_widget (w); |
| 315 | |
312 | 316 | dlg_select_by_id (w->owner, menubar->previous_widget); |
313 | 317 | do_refresh (); |
314 | 318 | } |
… |
… |
menubar_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) |
722 | 726 | /* menu bar is not active -- activate it */ |
723 | 727 | menubar->previous_widget = dlg_get_current_widget_id (w->owner); |
724 | 728 | menubar->is_active = TRUE; |
725 | | dlg_select_widget (w); |
| 729 | dlg_set_top_widget (w); |
726 | 730 | } |
727 | 731 | |
728 | 732 | menubar_remove (menubar); /* if already shown */ |
… |
… |
menubar_activate (WMenuBar * menubar, gboolean dropped, int which) |
1018 | 1022 | menubar->selected = (guint) which; |
1019 | 1023 | |
1020 | 1024 | menubar->previous_widget = dlg_get_current_widget_id (w->owner); |
1021 | | dlg_select_widget (w); |
| 1025 | |
| 1026 | /* Bring it to the top so it receives all mouse events before any other widget. |
| 1027 | * See also comment in menubar_finish(). */ |
| 1028 | dlg_set_top_widget (w); |
1022 | 1029 | } |
1023 | 1030 | } |
1024 | 1031 | |
diff --git a/lib/widget/mouse.c b/lib/widget/mouse.c
index 3bbec75..86c23b8 100644
a
|
b
|
mouse_process_event (Widget * w, mouse_event_t * event, gboolean click) |
196 | 196 | } |
197 | 197 | |
198 | 198 | /* --------------------------------------------------------------------------------------------- */ |
199 | | |
200 | | /** |
201 | | * Send mouse event directly to widget. |
202 | | * |
203 | | * @param event mouse event. event's coordinates are relative to widget's owner |
204 | | * @param w widget object. w's coordinates are global |
205 | | * |
206 | | * event's coordinates are relative to w->owner |
207 | | * w's coordinates are global |
208 | | */ |
209 | | void |
210 | | mouse_resend_event (mouse_event_t * event, Widget * w) |
211 | | { |
212 | | Widget *owner = WIDGET (w->owner); |
213 | | int wy, wx; |
214 | | int ey, ex; |
215 | | |
216 | | /* w's coordinates relative to owner */ |
217 | | wy = w->y - (owner != NULL ? owner->y : 0); |
218 | | wx = w->x - (owner != NULL ? owner->x : 0); |
219 | | /* save event's coordinates */ |
220 | | ey = event->y; |
221 | | ex = event->x; |
222 | | /* event's coordinates relative to w */ |
223 | | event->y -= wy; |
224 | | event->x -= wx; |
225 | | /* handle event in w */ |
226 | | w->mouse_callback (w, event->msg, event); |
227 | | /* restore event's coordinates for following handler */ |
228 | | event->y = ey; |
229 | | event->x = ex; |
230 | | } |
231 | | |
232 | | /* --------------------------------------------------------------------------------------------- */ |
diff --git a/lib/widget/mouse.h b/lib/widget/mouse.h
index 527b7de..aca8421 100644
a
|
b
|
typedef struct |
60 | 60 | mouse_event_t mouse_translate_event (Widget * w, Gpm_Event * event, gboolean * click); |
61 | 61 | /* Process high-level mouse event */ |
62 | 62 | int mouse_process_event (Widget * w, mouse_event_t * event, gboolean click); |
63 | | void mouse_resend_event (mouse_event_t * event, Widget * w); |
64 | 63 | |
65 | 64 | /*** inline functions ****************************************************************************/ |
66 | 65 | |
diff --git a/src/filemanager/midnight.c b/src/filemanager/midnight.c
index f95c254..ac83c6b 100644
a
|
b
|
midnight_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void |
1576 | 1576 | } |
1577 | 1577 | |
1578 | 1578 | /* --------------------------------------------------------------------------------------------- */ |
1579 | | |
1580 | | /** |
1581 | | * Handle mouse events of file manager screen. |
1582 | | * |
1583 | | * @param ww Widget object (the file manager) |
1584 | | * @param msg mouse event message |
1585 | | * @param event mouse event data |
1586 | | */ |
1587 | | static void |
1588 | | midnight_mouse_callback (Widget * ww, mouse_msg_t msg, mouse_event_t * event) |
1589 | | { |
1590 | | (void) ww; |
1591 | | |
1592 | | if (event->y == 0) |
1593 | | { |
1594 | | /* menubar */ |
1595 | | |
1596 | | if (menubar_visible || the_menubar->is_active) |
1597 | | mouse_resend_event (event, WIDGET (the_menubar)); |
1598 | | else |
1599 | | { |
1600 | | Widget *w; |
1601 | | |
1602 | | w = get_panel_widget (0); |
1603 | | if (w->mouse_callback != NULL) |
1604 | | mouse_resend_event (event, w); |
1605 | | |
1606 | | if (event->result.abort) |
1607 | | { |
1608 | | w = get_panel_widget (1); |
1609 | | if (w->mouse_callback != NULL) |
1610 | | { |
1611 | | event->result.abort = FALSE; |
1612 | | mouse_resend_event (event, w); |
1613 | | } |
1614 | | } |
1615 | | |
1616 | | if (event->result.abort) |
1617 | | { |
1618 | | event->result.abort = FALSE; |
1619 | | |
1620 | | if (msg == MSG_MOUSE_DOWN && (!menubar_visible || !the_menubar->is_active)) |
1621 | | menubar_activate (the_menubar, drop_menus != 0, -1); |
1622 | | |
1623 | | mouse_resend_event (event, WIDGET (the_menubar)); |
1624 | | } |
1625 | | } |
1626 | | } |
1627 | | else |
1628 | | { |
1629 | | /* Continue handling of unhandled event in panel or menu */ |
1630 | | event->result.abort = TRUE; |
1631 | | } |
1632 | | } |
1633 | | |
1634 | | /* --------------------------------------------------------------------------------------------- */ |
1635 | 1579 | /*** public functions ****************************************************************************/ |
1636 | 1580 | /* --------------------------------------------------------------------------------------------- */ |
1637 | 1581 | |
… |
… |
do_nc (void) |
1814 | 1758 | #endif |
1815 | 1759 | |
1816 | 1760 | midnight_dlg = dlg_create (FALSE, 0, 0, LINES, COLS, dialog_colors, midnight_callback, |
1817 | | midnight_mouse_callback, "[main]", NULL, DLG_NONE); |
| 1761 | NULL, "[main]", NULL, DLG_NONE); |
1818 | 1762 | |
1819 | 1763 | /* Check if we were invoked as an editor or file viewer */ |
1820 | 1764 | if (mc_global.mc_run_mode != MC_RUN_FULL) |