Some fixes for the submenu hide delay code

When nothing in a menu is selected, go back to selecting the open submenu.
Adjust the LeaveNotify event handling to only respond when there is not a
  EnterNotify coming for the same menu frame.
Change the default submenu show/hide delays.
Have the default values for submenu show/hide match the default rc.xml
This commit is contained in:
Dana Jansens 2010-01-04 15:07:10 -05:00
parent 8968b38338
commit b88cf22bc0
5 changed files with 72 additions and 87 deletions

View file

@ -632,7 +632,7 @@
menu is hidden again -->
<middle>no</middle>
<!-- center submenus vertically about the parent entry -->
<submenuShowDelay>100</submenuShowDelay>
<submenuShowDelay>200</submenuShowDelay>
<!-- this one is easy, time to delay before showing a submenu after hovering
over the parent entry -->
<submenuHideDelay>400</submenuHideDelay>

View file

@ -1018,8 +1018,8 @@ void config_startup(ObParseInst *i)
config_menu_hide_delay = 250;
config_menu_middle = FALSE;
config_submenu_show_delay = 0;
config_submenu_hide_delay = 750;
config_submenu_show_delay = 200;
config_submenu_hide_delay = 400;
config_menu_client_list_icons = TRUE;
config_menu_manage_desktops = TRUE;
config_menu_files = NULL;

View file

@ -1800,6 +1800,15 @@ static gboolean event_handle_menu_keyboard(XEvent *ev)
return ret;
}
static Bool event_look_for_menu_enter(Display *d, XEvent *ev, XPointer arg)
{
ObMenuFrame *f = (ObMenuFrame*)arg;
ObMenuEntryFrame *e;
return ev->type == EnterNotify &&
(e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
!e->ignore_enters && e->frame == f;
}
static gboolean event_handle_menu(XEvent *ev)
{
ObMenuFrame *f;
@ -1837,11 +1846,17 @@ static gboolean event_handle_menu(XEvent *ev)
if (ev->xcrossing.detail == NotifyInferior)
break;
if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
(f = find_active_menu()) && f->selected == e &&
e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)))
{
menu_frame_select(e->frame, NULL, FALSE);
XEvent ce;
/* check if an EnterNotify event is coming, and if not, then select
nothing in the menu */
if (XCheckIfEvent(ob_display, &ce, event_look_for_menu_enter,
(XPointer)e->frame))
XPutBackEvent(ob_display, &ce);
else
menu_frame_select(e->frame, NULL, FALSE);
}
break;
case MotionNotify:

View file

@ -49,10 +49,10 @@ static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
ObMenuFrame *frame);
static void menu_entry_frame_free(ObMenuEntryFrame *self);
static void menu_frame_update(ObMenuFrame *self);
static gboolean menu_entry_frame_submenu_timeout(gpointer data);
static gboolean submenu_show_timeout(gpointer data);
static void menu_frame_hide(ObMenuFrame *self);
static gboolean menu_entry_frame_submenu_hide_timeout(gpointer data);
static gboolean submenu_hide_timeout(gpointer data);
static Window createWindow(Window parent, gulong mask,
XSetWindowAttributes *attrib)
@ -974,12 +974,13 @@ gboolean menu_frame_show_topmenu(ObMenuFrame *self, gint x, gint y,
return TRUE;
}
static void remove_submenu_hide_timeout(ObMenuFrame *self /* parent of submenu to hide */)
/*! Stop hiding an open submenu.
@child The OnMenuFrame of the submenu to be hidden
*/
static void remove_submenu_hide_timeout(ObMenuFrame *child)
{
ob_main_loop_timeout_remove(ob_main_loop,
menu_entry_frame_submenu_hide_timeout);
if (self)
self->submenu_to_hide = NULL;
ob_main_loop_timeout_remove_data(ob_main_loop, submenu_hide_timeout,
child, FALSE);
}
gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent,
@ -995,13 +996,12 @@ gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent,
self->parent = parent;
self->parent_entry = parent_entry;
remove_submenu_hide_timeout(parent);
/* set up parent's child to be us */
if ((parent->child) != self) {
if (parent->child)
menu_frame_hide(parent->child);
parent->child = self;
parent->child_entry = parent_entry;
}
if (!menu_frame_show(self))
@ -1034,8 +1034,6 @@ static void menu_frame_hide(ObMenuFrame *self)
GList *it = g_list_find(menu_frame_visible, self);
gulong ignore_start;
remove_submenu_hide_timeout(self->parent);
if (!it)
return;
@ -1045,8 +1043,12 @@ static void menu_frame_hide(ObMenuFrame *self)
if (self->child)
menu_frame_hide(self->child);
if (self->parent)
if (self->parent) {
remove_submenu_hide_timeout(self);
self->parent->child = NULL;
self->parent->child_entry = NULL;
}
self->parent = NULL;
self->parent_entry = NULL;
@ -1071,8 +1073,7 @@ void menu_frame_hide_all(void)
if (config_submenu_show_delay) {
/* remove any submenu open requests */
ob_main_loop_timeout_remove(ob_main_loop,
menu_entry_frame_submenu_timeout);
ob_main_loop_timeout_remove(ob_main_loop, submenu_show_timeout);
}
if ((it = g_list_last(menu_frame_visible)))
menu_frame_hide(it->data);
@ -1087,7 +1088,7 @@ void menu_frame_hide_all_client(ObClient *client)
if (config_submenu_show_delay) {
/* remove any submenu open requests */
ob_main_loop_timeout_remove(ob_main_loop,
menu_entry_frame_submenu_timeout);
submenu_show_timeout);
}
menu_frame_hide(f);
}
@ -1132,15 +1133,16 @@ ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y)
return ret;
}
static gboolean menu_entry_frame_submenu_timeout(gpointer data)
static gboolean submenu_show_timeout(gpointer data)
{
g_assert(menu_frame_visible);
menu_entry_frame_show_submenu((ObMenuEntryFrame*)data);
return FALSE;
}
static gboolean menu_entry_frame_submenu_hide_timeout(gpointer data)
static gboolean submenu_hide_timeout(gpointer data)
{
g_assert(menu_frame_visible);
menu_frame_hide((ObMenuFrame*)data);
return FALSE;
}
@ -1150,49 +1152,24 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry,
{
ObMenuEntryFrame *old = self->selected;
ObMenuFrame *oldchild = self->child;
ObMenuEntryFrame *oldchild_entry = self->child_entry;
ObMenuEntryFrame *temp;
gboolean reselection;
if (!oldchild) {
/* self is the last visible (sub)menu */
if (self->parent && self->parent_entry != self->parent->selected) {
/* Legend:
(config_submenu_hide_delay != 0)
In the parent menu corresponding entry "A" selected,
this submenu ('self') shown, cursor moved in the parent
menu to another entry "B", then cursor moved for the
first time into this submenu.
Results:
parent menu selection is "B" instead of "A",
*/
temp = self->parent->selected;
self->parent->selected = self->parent_entry;
if (temp)
menu_entry_frame_render(temp);
menu_entry_frame_render(self->parent_entry);
}
remove_submenu_hide_timeout(self->parent);
}
else if (oldchild->child) {
/* self is the (at least) grandparent of the last visible submenu */
menu_frame_hide(oldchild->child);
if (temp = oldchild->selected) {
oldchild->selected = NULL;
menu_entry_frame_render(temp);
}
}
/* if the user selected a separator, ignore it and reselect what we had
selected before */
if (entry && entry->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR)
entry = old;
if (old == entry) return;
/* if the user left this menu but we have a submenu open, move the
selection back to that submenu */
if (!entry && oldchild_entry)
entry = oldchild_entry;
if (config_submenu_show_delay) {
/* remove any submenu open requests */
ob_main_loop_timeout_remove(ob_main_loop,
menu_entry_frame_submenu_timeout);
ob_main_loop_timeout_remove(ob_main_loop, submenu_show_timeout);
}
self->selected = entry;
@ -1200,49 +1177,44 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry,
if (old)
menu_entry_frame_render(old);
reselection = FALSE;
if (oldchild) {
if (self->submenu_to_hide == entry) {
/* Legend:
(config_submenu_hide_delay != 0)
Some entry "A" selected; corresponding submenu shown;
cursor moved to another entry "B" and moved back
to the entry "A", when submenu hide request added,
but submenu not hided.
*/
reselection = TRUE;
remove_submenu_hide_timeout(self);
if (oldchild_entry) {
/* There is an open submenu */
if (oldchild_entry == self->selected) {
/* The open submenu has been reselected, so stop hiding the
submenu */
remove_submenu_hide_timeout(oldchild);
}
else if (!immediate && config_submenu_hide_delay) {
if (self->submenu_to_hide == NULL) {
else if (oldchild_entry == old) {
/* The open submenu was selected and is no longer, so hide the
submenu */
if (!immediate && config_submenu_hide_delay) {
ob_main_loop_timeout_add(ob_main_loop,
config_submenu_hide_delay * 1000,
menu_entry_frame_submenu_hide_timeout,
oldchild, g_direct_equal,
NULL);
self->submenu_to_hide = old;
config_submenu_hide_delay * 1000,
submenu_hide_timeout,
oldchild, g_direct_equal,
NULL);
}
else
menu_frame_hide(oldchild);
}
else
menu_frame_hide(oldchild);
}
if (self->selected) {
menu_entry_frame_render(self->selected);
if (!reselection &&
(self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU))
/* only show if the submenu isn't already showing */
if (oldchild_entry != self->selected &&
self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
{
if (config_submenu_show_delay && !immediate) {
/* initiate a new submenu open request */
ob_main_loop_timeout_add(ob_main_loop,
config_submenu_show_delay * 1000,
menu_entry_frame_submenu_timeout,
submenu_show_timeout,
self->selected, g_direct_equal,
NULL);
} else {
} else
menu_entry_frame_show_submenu(self->selected);
}
}
}
}

View file

@ -50,6 +50,7 @@ struct _ObMenuFrame
ObMenuFrame *parent;
ObMenuEntryFrame *parent_entry;
ObMenuFrame *child;
ObMenuEntryFrame *child_entry;
GList *entries;
ObMenuEntryFrame *selected;
@ -80,9 +81,6 @@ struct _ObMenuFrame
menu until it has seen a KeyPress. this is to
avoid having the keybinding used to show the menu
end up running something inside the menu */
ObMenuEntryFrame * submenu_to_hide; /* if exist (single!) submenu hide request
then this variable hold a copy of 'selected' field of the parent menu,
otherwice NULL */
};
struct _ObMenuEntryFrame