Skip to content

Commit

Permalink
nemo-window.c: Fix hidden menu alt-key activation.
Browse files Browse the repository at this point in the history
The existing behavior for alt-activation of the menu was interfering
with other shortcuts, including window/workspace navigation, and
generally was counterproductive.

New behavior has the menu showing on alt release, and hiding on press.
Additionally, the showing will grab the focus so a mnemonic can be
typed to activate one of the submenus.  Extraneous keystrokes between
a single alt press and release will cause a pending menu show to be
skipped, so we don't get the menu appearing when an unrelated shortcut,
that involves the alt key as a modifier, is activated.

This behavior brings it more in line with other applications that offer
hidden menubar behavior (such as firefox).

Fixes #1389
Fixes #1895
  • Loading branch information
mtwebster committed Dec 2, 2018
1 parent 98b7811 commit f40311d
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 34 deletions.
8 changes: 4 additions & 4 deletions src/nemo-window-private.h
Expand Up @@ -90,9 +90,8 @@ struct NemoWindowDetails
guint extensions_toolbar_merge_id;
GtkActionGroup *extensions_toolbar_action_group;

/* focus widget before the location bar has been shown temporarily */
GtkWidget *last_focus_widget;

guint menu_hide_delay_id;

/* split view */
GtkWidget *split_view_hpane;

Expand All @@ -102,7 +101,8 @@ struct NemoWindowDetails

guint menu_state_changed_id;

gboolean temporary_menu_bar;
gboolean menu_skip_release;
gboolean menu_show_queued;

gchar *ignore_meta_view_id;
gint ignore_meta_zoom_level;
Expand Down
173 changes: 143 additions & 30 deletions src/nemo-window.c
Expand Up @@ -87,7 +87,8 @@ static void mouse_back_button_changed (gpointer callback_
static void mouse_forward_button_changed (gpointer callback_data);
static void use_extra_mouse_buttons_changed (gpointer callback_data);
static void side_pane_id_changed (NemoWindow *window);
static void handle_alt_menu_key (NemoWindow *window, gboolean on_release);
static void toggle_menubar (NemoWindow *window,
gint action);

/* Sanity check: highest mouse button value I could find was 14. 5 is our
* lower threshold (well-documented to be the one of the button events for the
Expand Down Expand Up @@ -116,6 +117,12 @@ enum {
LAST_SIGNAL
};

enum {
MENU_HIDE,
MENU_SHOW,
MENU_TOGGLE
};

static guint signals[LAST_SIGNAL] = { 0 };
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };

Expand Down Expand Up @@ -518,12 +525,51 @@ static gboolean
on_button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
NemoWindow *window = NEMO_WINDOW (user_data);

if (event->button == 3) {
/* simulate activating the menu */
handle_alt_menu_key (window, FALSE);
handle_alt_menu_key (window, TRUE);
toggle_menubar (window, MENU_TOGGLE);
}

return GDK_EVENT_STOP;
}

static void
clear_menu_hide_delay (NemoWindow *window)
{
if (window->details->menu_hide_delay_id > 0) {
g_source_remove (window->details->menu_hide_delay_id);
}
return TRUE;

window->details->menu_hide_delay_id = 0;
}

static gboolean
hide_menu_on_delay (NemoWindow *window)
{
toggle_menubar (window, MENU_HIDE);

window->details->menu_hide_delay_id = 0;
return FALSE;
}

static gboolean
on_menu_focus_out (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
{
NemoWindow *window = NEMO_WINDOW (user_data);

/* The menu, when visible on demand, gets the keyboard grab.
* If the user clicks on some element in the window,, we want the menu
* to disappear, but if it's done immediately, everything shifts up the
* height of the menu, and the user will more than likely end up clicking
* in the wrong spot. Delay the hide momentarily, to allow the user to
* complete their click action. */
clear_menu_hide_delay (window);

window->details->menu_hide_delay_id = g_timeout_add (200, (GSourceFunc) hide_menu_on_delay, window);

return GDK_EVENT_PROPAGATE;
}

static void
Expand Down Expand Up @@ -559,14 +605,20 @@ nemo_window_constructed (GObject *self)
/* Statusbar is packed in the subclasses */

nemo_window_initialize_menus (window);

window->details->temporary_menu_bar = FALSE;

nemo_window_initialize_actions (window);

menu = gtk_ui_manager_get_widget (window->details->ui_manager, "/MenuBar");
window->details->menubar = menu;

gtk_widget_set_can_focus (menu, TRUE);
gtk_widget_set_hexpand (menu, TRUE);

g_signal_connect_object (menu,
"focus-out-event",
G_CALLBACK (on_menu_focus_out),
window,
0);

if (g_settings_get_boolean (nemo_window_state, NEMO_WINDOW_STATE_START_WITH_MENU_BAR)){
gtk_widget_show (menu);
} else {
Expand Down Expand Up @@ -771,6 +823,8 @@ nemo_window_finalize (GObject *object)
window->details->sidebar_width_handler_id = 0;
}

clear_menu_hide_delay (window);

nemo_window_finalize_menus (window);

g_clear_object (&window->details->nav_state);
Expand Down Expand Up @@ -1021,30 +1075,61 @@ nemo_window_realize (GtkWidget *widget)
}

static void
handle_alt_menu_key (NemoWindow *window,
gboolean on_release)
toggle_menubar (NemoWindow *window, gint action)
{
GtkWidget *menu = window->details->menubar;
GtkWidget *menu;
gboolean default_visible;

default_visible = g_settings_get_boolean (nemo_window_state,
NEMO_WINDOW_STATE_START_WITH_MENU_BAR);

gboolean default_visible = g_settings_get_boolean (nemo_window_state,
NEMO_WINDOW_STATE_START_WITH_MENU_BAR);
menu = window->details->menubar;

if (default_visible || window->details->disable_chrome)
if (default_visible || window->details->disable_chrome) {
return;
}

gboolean visible = gtk_widget_get_visible (menu);
if (action == MENU_TOGGLE) {
action = gtk_widget_get_visible (menu) ? MENU_HIDE : MENU_SHOW;
}

if (!visible) {
if (action == MENU_HIDE) {
gtk_widget_hide (menu);
} else {
gtk_widget_show (menu);
window->details->temporary_menu_bar = FALSE;
} else if (visible && on_release) {
if (!window->details->temporary_menu_bar)
window->details->temporary_menu_bar = TRUE;
else {
gtk_widget_hide (menu);
window->details->temporary_menu_bar = FALSE;
}

/* When the menu is normally hidden, have an activation of it trigger a key grab.
* For keyboard users, this is a natural progression, that they will type a mnemonic
* next to open a menu. Any loss of focus or click elsewhere will re-hide the menu
* and cancel focus.
*/
gtk_widget_grab_focus (menu);
gtk_window_set_mnemonics_visible (GTK_WINDOW (window), TRUE);
}

return;
}

static gboolean
is_alt_key_event (GdkEventKey *event)
{
GdkModifierType nominal_state;
gboolean state_ok;

nominal_state = event->state & gtk_accelerator_get_default_mod_mask();

/* A key press of alt will show just the alt keyval (GDK_KEY_Alt_L/R). A key release
* of a single modifier is always modified by itself. So a valid press state is 0 and
* a valid release state is GDK_MOD1_MASK (alt modifier).
*/
state_ok = (event->type == GDK_KEY_PRESS && nominal_state == 0) ||
(event->type == GDK_KEY_RELEASE && nominal_state == GDK_MOD1_MASK);

if (state_ok && (event->keyval == GDK_KEY_Alt_L || event->keyval == GDK_KEY_Alt_R)) {
return TRUE;
}

return FALSE;
}

static gboolean
Expand Down Expand Up @@ -1107,8 +1192,25 @@ nemo_window_key_press_event (GtkWidget *widget,
}
}

if (event->keyval == GDK_KEY_Alt_L || event->keyval == GDK_KEY_Alt_R) {
handle_alt_menu_key (window, FALSE);
/* An alt key press by itself will always hide the menu if it's visible. We set a flag
* to skip the subsequent release, otherwise we'll show the menu again.
*
* When alt is pressed and the menu is NOT visible, we flag that on release we'll show the
* menu. If any other keys are pressed between alt being pressed and released, we clear that
* flag, because it was more than likely part of some other shortcut, and otherwise, depending
* on the order the keys are released, if the alt key is last to be released, we don't want to
* show the menu, as that was not the original intent.
*/

if (is_alt_key_event (event)) {
if (gtk_widget_get_visible (window->details->menubar)) {
toggle_menubar (window, MENU_HIDE);
window->details->menu_skip_release = TRUE;
} else {
window->details->menu_show_queued = TRUE;
}
} else {
window->details->menu_show_queued = FALSE;
}

return GTK_WIDGET_CLASS (nemo_window_parent_class)->key_press_event (widget, event);
Expand All @@ -1120,10 +1222,20 @@ nemo_window_key_release_event (GtkWidget *widget,
{
NemoWindow *window = NEMO_WINDOW (widget);

if (event->keyval == GDK_KEY_Alt_L || event->keyval == GDK_KEY_Alt_R) {
handle_alt_menu_key (window, TRUE);
/* Conditions to show the menu via the alt key is that it must have been pressed and
* released without any other key events in between, and we must not have hidden the
* menu on the alt key press event. Show we check both flags here, for opposing states.
*/

if (is_alt_key_event (event)) {
if (!window->details->menu_skip_release && window->details->menu_show_queued) {
toggle_menubar (window, MENU_SHOW);
}
}

window->details->menu_skip_release = FALSE;
window->details->menu_show_queued = FALSE;

return GTK_WIDGET_CLASS (nemo_window_parent_class)->key_release_event (widget, event);
}

Expand Down Expand Up @@ -1195,8 +1307,6 @@ nemo_window_sync_menu_bar (NemoWindow *window)
} else {
gtk_widget_hide (menu);
}

window->details->temporary_menu_bar = FALSE;
}

void
Expand Down Expand Up @@ -1827,6 +1937,9 @@ nemo_window_init (NemoWindow *window)
window->details->show_sidebar = g_settings_get_boolean (nemo_window_state,
NEMO_WINDOW_STATE_START_WITH_SIDEBAR);

window->details->menu_skip_release = FALSE;
window->details->menu_show_queued = FALSE;

window->details->ignore_meta_view_id = NULL;
window->details->ignore_meta_zoom_level = -1;
window->details->ignore_meta_visible_columns = NULL;
Expand Down

0 comments on commit f40311d

Please sign in to comment.