[gnome-flashback] desktop: add keyboard navigation



commit ed79bc1cea0a5b585d8afe86dd126483d2073961
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Sun Nov 17 21:23:13 2019 +0200

    desktop: add keyboard navigation

 gnome-flashback/libdesktop/gf-icon-view.c | 488 +++++++++++++++++++++++++++---
 gnome-flashback/libdesktop/gf-icon.c      |   4 +
 2 files changed, 448 insertions(+), 44 deletions(-)
---
diff --git a/gnome-flashback/libdesktop/gf-icon-view.c b/gnome-flashback/libdesktop/gf-icon-view.c
index dcf8655..c0937cd 100644
--- a/gnome-flashback/libdesktop/gf-icon-view.c
+++ b/gnome-flashback/libdesktop/gf-icon-view.c
@@ -77,6 +77,9 @@ struct _GfIconView
   GfFileManagerGen *file_manager;
 
   GtkWidget        *create_folder_dialog;
+
+  GfIcon           *last_selected_icon;
+  GfIcon           *extend_from_icon;
 };
 
 enum
@@ -85,6 +88,9 @@ enum
   UNSELECT_ALL,
 
   ACTIVATE,
+  TOGGLE,
+
+  MOVE,
 
   LAST_SIGNAL
 };
@@ -450,6 +456,23 @@ icon_selected_cb (GfIcon     *icon,
     self->selected_icons = g_list_remove (self->selected_icons, icon);
 }
 
+static void
+icon_has_focus_cb (GtkWidget  *widget,
+                   GParamSpec *pspec,
+                   GfIconView *self)
+{
+  if (gtk_widget_has_focus (widget))
+    self->last_selected_icon = GF_ICON (widget);
+}
+
+static void
+icon_clicked_cb (GtkWidget  *widget,
+                 GfIconView *self)
+{
+  self->last_selected_icon = GF_ICON (widget);
+  self->extend_from_icon = NULL;
+}
+
 static void
 show_item_properties_cb (GObject      *object,
                          GAsyncResult *res,
@@ -515,6 +538,14 @@ create_icon_info (GfIconView *self,
                     G_CALLBACK (icon_selected_cb),
                     self);
 
+  g_signal_connect (icon, "notify::has-focus",
+                    G_CALLBACK (icon_has_focus_cb),
+                    self);
+
+  g_signal_connect (icon, "clicked",
+                    G_CALLBACK (icon_clicked_cb),
+                    self);
+
   g_settings_bind (self->settings, "icon-size",
                    icon, "icon-size",
                    G_SETTINGS_BIND_GET);
@@ -587,6 +618,30 @@ find_icon_info_by_file (GfIconView *self,
   return NULL;
 }
 
+static void
+remove_icon_from_view (GfIconView *self,
+                       GfIconInfo *info)
+{
+  GfIcon *icon;
+
+  if (info->view == NULL)
+    return;
+
+  icon = GF_ICON (info->icon);
+
+  gf_monitor_view_remove_icon (GF_MONITOR_VIEW (info->view), GTK_WIDGET (icon));
+  info->view = NULL;
+
+  self->selected_icons = g_list_remove (self->selected_icons, icon);
+  self->rubberband_icons = g_list_remove (self->rubberband_icons, icon);
+
+  if (icon == self->last_selected_icon)
+    self->last_selected_icon = NULL;
+
+  if (icon == self->extend_from_icon)
+    self->extend_from_icon = NULL;
+}
+
 static void
 file_deleted (GfIconView *self,
               GFile      *deleted_file)
@@ -598,18 +653,7 @@ file_deleted (GfIconView *self,
   if (info == NULL)
     return;
 
-  if (info->view != NULL)
-    {
-      GtkWidget *icon;
-
-      icon = info->icon;
-
-      gf_monitor_view_remove_icon (GF_MONITOR_VIEW (info->view), icon);
-      info->view = NULL;
-
-      self->selected_icons = g_list_remove (self->selected_icons, icon);
-      self->rubberband_icons = g_list_remove (self->rubberband_icons, icon);
-    }
+  remove_icon_from_view (self, info);
 
   self->icons = g_list_remove (self->icons, info);
   gf_icon_info_free (info);
@@ -1168,6 +1212,9 @@ multi_press_pressed_cb (GtkGestureMultiPress *gesture,
   if (event == NULL)
     return;
 
+  if (!gtk_widget_has_focus (GTK_WIDGET (self)))
+    gtk_widget_grab_focus (GTK_WIDGET (self));
+
   if (button == GDK_BUTTON_PRIMARY)
     {
       GdkModifierType state;
@@ -1775,22 +1822,7 @@ show_home_changed_cb (GSettings  *settings,
     }
   else if (self->home_info != NULL)
     {
-      GfIconInfo *info;
-
-      info = self->home_info;
-
-      if (info->view != NULL)
-        {
-          GtkWidget *icon;
-
-          icon = info->icon;
-
-          gf_monitor_view_remove_icon (GF_MONITOR_VIEW (info->view), icon);
-          info->view = NULL;
-
-          self->selected_icons = g_list_remove (self->selected_icons, icon);
-          self->rubberband_icons = g_list_remove (self->rubberband_icons, icon);
-        }
+      remove_icon_from_view (self, self->home_info);
 
       self->icons = g_list_remove (self->icons, self->home_info);
       gf_icon_info_free (self->home_info);
@@ -1815,22 +1847,7 @@ show_trash_changed_cb (GSettings  *settings,
     }
   else if (self->trash_info != NULL)
     {
-      GfIconInfo *info;
-
-      info = self->trash_info;
-
-      if (info->view != NULL)
-        {
-          GtkWidget *icon;
-
-          icon = info->icon;
-
-          gf_monitor_view_remove_icon (GF_MONITOR_VIEW (info->view), icon);
-          info->view = NULL;
-
-          self->selected_icons = g_list_remove (self->selected_icons, icon);
-          self->rubberband_icons = g_list_remove (self->rubberband_icons, icon);
-        }
+      remove_icon_from_view (self, self->trash_info);
 
       self->icons = g_list_remove (self->icons, self->trash_info);
       gf_icon_info_free (self->trash_info);
@@ -1928,6 +1945,315 @@ activate_cb (GfIconView *self,
     gf_icon_open (GF_ICON (l->data));
 }
 
+static void
+toggle_cb (GfIconView *self,
+           gpointer    user_data)
+{
+  if (self->last_selected_icon == NULL)
+    return;
+
+  if (gf_icon_get_selected (self->last_selected_icon))
+    gf_icon_set_selected (self->last_selected_icon, FALSE);
+  else
+    gf_icon_set_selected (self->last_selected_icon, TRUE);
+}
+
+static GfMonitorView *
+find_monitor_view_up (GfIconView   *self,
+                      GList        *views,
+                      GdkRectangle *current)
+{
+  GList *l;
+
+  for (l = views; l != NULL; l = l->next)
+    {
+      GfMonitorView *view;
+      GdkMonitor *monitor;
+      GdkRectangle geometry;
+
+      view = GF_MONITOR_VIEW (l->data);
+      monitor = gf_monitor_view_get_monitor (view);
+
+      gdk_monitor_get_geometry (monitor, &geometry);
+
+      if (current->y == geometry.y + geometry.height)
+        return view;
+    }
+
+  return NULL;
+}
+
+static GfMonitorView *
+find_monitor_view_down (GfIconView   *self,
+                        GList        *views,
+                        GdkRectangle *current)
+{
+  GList *l;
+
+  for (l = views; l != NULL; l = l->next)
+    {
+      GfMonitorView *view;
+      GdkMonitor *monitor;
+      GdkRectangle geometry;
+
+      view = GF_MONITOR_VIEW (l->data);
+      monitor = gf_monitor_view_get_monitor (view);
+
+      gdk_monitor_get_geometry (monitor, &geometry);
+
+      if (current->y + current->height == geometry.y)
+        return view;
+    }
+
+  return NULL;
+}
+
+static GfMonitorView *
+find_monitor_view_left (GfIconView   *self,
+                        GList        *views,
+                        GdkRectangle *current)
+{
+  GList *l;
+
+  for (l = views; l != NULL; l = l->next)
+    {
+      GfMonitorView *view;
+      GdkMonitor *monitor;
+      GdkRectangle geometry;
+
+      view = GF_MONITOR_VIEW (l->data);
+      monitor = gf_monitor_view_get_monitor (view);
+
+      gdk_monitor_get_geometry (monitor, &geometry);
+
+      if (current->x == geometry.x + geometry.width)
+        return view;
+    }
+
+  return NULL;
+}
+
+static GfMonitorView *
+find_monitor_view_right (GfIconView   *self,
+                         GList        *views,
+                         GdkRectangle *current)
+{
+  GList *l;
+
+  for (l = views; l != NULL; l = l->next)
+    {
+      GfMonitorView *view;
+      GdkMonitor *monitor;
+      GdkRectangle geometry;
+
+      view = GF_MONITOR_VIEW (l->data);
+      monitor = gf_monitor_view_get_monitor (view);
+
+      gdk_monitor_get_geometry (monitor, &geometry);
+
+      if (current->x + current->width == geometry.x)
+        return view;
+    }
+
+  return NULL;
+}
+
+static GfMonitorView *
+find_next_view (GfIconView       *self,
+                GfMonitorView    *next_to,
+                GtkDirectionType  direction)
+{
+  GList *views;
+  GList *children;
+  GList *l;
+  GdkMonitor *monitor;
+  GdkRectangle geometry;
+  GfMonitorView *next_view;
+
+  views = NULL;
+
+  children = gtk_container_get_children (GTK_CONTAINER (self->fixed));
+  for (l = children; l != NULL; l = l->next)
+    {
+      GfMonitorView *view;
+
+      view = GF_MONITOR_VIEW (l->data);
+      if (view == next_to)
+        continue;
+
+      views = g_list_prepend (views, view);
+    }
+
+  g_list_free (children);
+
+  if (views == NULL)
+    return NULL;
+
+  monitor = gf_monitor_view_get_monitor (next_to);
+  gdk_monitor_get_geometry (monitor, &geometry);
+
+  next_view = NULL;
+
+  switch (direction)
+    {
+      case GTK_DIR_UP:
+        next_view = find_monitor_view_up (self, views, &geometry);
+        break;
+
+      case GTK_DIR_DOWN:
+        next_view = find_monitor_view_down (self, views, &geometry);
+        break;
+
+      case GTK_DIR_LEFT:
+        next_view = find_monitor_view_left (self, views, &geometry);
+        break;
+
+      case GTK_DIR_RIGHT:
+        next_view = find_monitor_view_right (self, views, &geometry);
+        break;
+
+      case GTK_DIR_TAB_FORWARD:
+      case GTK_DIR_TAB_BACKWARD:
+      default:
+        break;
+    }
+
+  g_list_free (views);
+
+  return next_view;
+}
+
+static void
+move_cb (GfIconView       *self,
+         GtkDirectionType  direction,
+         gpointer          user_data)
+{
+  GdkModifierType state;
+  gboolean extend;
+  gboolean modify;
+  GfMonitorView *view;
+  GfIcon *next_icon;
+
+  extend = FALSE;
+  modify = FALSE;
+
+  if (self->icons == NULL)
+    return;
+
+  if (gtk_get_current_event_state (&state))
+    {
+      GdkModifierType extend_mod_mask;
+      GdkModifierType modify_mod_mask;
+
+      extend_mod_mask =
+        gtk_widget_get_modifier_mask (GTK_WIDGET (self),
+                                      GDK_MODIFIER_INTENT_EXTEND_SELECTION);
+      modify_mod_mask =
+        gtk_widget_get_modifier_mask (GTK_WIDGET (self),
+                                      GDK_MODIFIER_INTENT_MODIFY_SELECTION);
+
+      if ((state & extend_mod_mask) == extend_mod_mask)
+        extend = TRUE;
+
+      if ((state & modify_mod_mask) == modify_mod_mask)
+        modify = TRUE;
+    }
+
+  if (self->last_selected_icon != NULL)
+    {
+      GList *l;
+
+      for (l = self->icons; l != NULL; l = l->next)
+        {
+          GfIconInfo *info;
+
+          info = (GfIconInfo *) l->data;
+
+          if (GF_ICON (info->icon) == self->last_selected_icon)
+            {
+              view = GF_MONITOR_VIEW (info->view);
+              break;
+            }
+        }
+    }
+  else
+    {
+      GList *children;
+      GList *l;
+
+      children = gtk_container_get_children (GTK_CONTAINER (self->fixed));
+
+      for (l = children; l != NULL; l = l->next)
+        {
+          GfMonitorView *tmp;
+
+          tmp = GF_MONITOR_VIEW (l->data);
+
+          if (gf_monitor_view_is_primary (tmp))
+            {
+              view = tmp;
+              break;
+            }
+        }
+
+      g_list_free (children);
+    }
+
+  g_assert (view != NULL);
+
+  do
+    {
+      next_icon = gf_monitor_view_find_next_icon (view,
+                                                  self->last_selected_icon,
+                                                  direction);
+
+      if (next_icon == NULL)
+        view = find_next_view (self, view, direction);
+    }
+  while (next_icon == NULL && view != NULL);
+
+  if (next_icon == NULL)
+    {
+      gtk_widget_error_bell (GTK_WIDGET (self));
+      return;
+    }
+
+  if (!modify)
+    unselect_icons (self);
+
+  if (extend)
+    {
+      GdkRectangle rect1;
+      GdkRectangle rect2;
+      GdkRectangle area;
+      GList *children;
+      GList *l;
+
+      if (self->extend_from_icon == NULL)
+         self->extend_from_icon = self->last_selected_icon;
+
+      gtk_widget_get_allocation (GTK_WIDGET (self->extend_from_icon), &rect1);
+      gtk_widget_get_allocation (GTK_WIDGET (next_icon), &rect2);
+
+      gdk_rectangle_union (&rect1, &rect2, &area);
+
+      children = gtk_container_get_children (GTK_CONTAINER (self->fixed));
+
+      for (l = children; l != NULL; l = l->next)
+        gf_monitor_view_select_icons (GF_MONITOR_VIEW (l->data), &area);
+
+      g_list_free (children);
+    }
+
+  if (!extend && !modify)
+    {
+      self->extend_from_icon = NULL;
+      gf_icon_set_selected (next_icon, TRUE);
+    }
+
+  gtk_widget_grab_focus (GTK_WIDGET (next_icon));
+}
+
 static GtkWidget *
 create_dummy_icon (GfIconView *self)
 {
@@ -2105,6 +2431,36 @@ install_signals (void)
     g_signal_new ("activate", GF_TYPE_ICON_VIEW,
                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                   0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+  view_signals[TOGGLE] =
+    g_signal_new ("toggle", GF_TYPE_ICON_VIEW,
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+  view_signals[MOVE] =
+    g_signal_new ("move", GF_TYPE_ICON_VIEW,
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  0, NULL, NULL, NULL, G_TYPE_NONE, 1,
+                  GTK_TYPE_DIRECTION_TYPE);
+}
+
+static void
+add_move_binding (GtkBindingSet    *binding_set,
+                  guint             keyval,
+                  GdkModifierType   modifiers,
+                  GtkDirectionType  direction)
+{
+  gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
+                                "move", 1,
+                                GTK_TYPE_DIRECTION_TYPE, direction);
+
+  gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
+                                "move", 1,
+                                GTK_TYPE_DIRECTION_TYPE, direction);
+
+  gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
+                                "move", 1,
+                                GTK_TYPE_DIRECTION_TYPE, direction);
 }
 
 static void
@@ -2112,14 +2468,20 @@ add_bindings (GtkBindingSet *binding_set)
 {
   GdkModifierType modifiers;
 
+  /* Select all */
+
   modifiers = GDK_CONTROL_MASK;
   gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, modifiers,
                                 "select-all", 0);
 
+  /* Unselect all */
+
   modifiers = GDK_CONTROL_MASK | GDK_SHIFT_MASK;
   gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, modifiers,
                                 "unselect-all", 0);
 
+  /* Activate */
+
   modifiers = 0;
   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, modifiers,
                                 "activate", 0);
@@ -2131,6 +2493,42 @@ add_bindings (GtkBindingSet *binding_set)
   modifiers = 0;
   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, modifiers,
                                 "activate", 0);
+
+  /* Toggle */
+
+  modifiers = GDK_CONTROL_MASK;
+  gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, modifiers,
+                                "toggle", 0);
+
+  modifiers = GDK_CONTROL_MASK;
+  gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, modifiers,
+                                "toggle", 0);
+
+  /* Move */
+
+  modifiers = 0;
+  add_move_binding (binding_set, GDK_KEY_Up, modifiers, GTK_DIR_UP);
+
+  modifiers = 0;
+  add_move_binding (binding_set, GDK_KEY_KP_Up, modifiers, GTK_DIR_UP);
+
+  modifiers = 0;
+  add_move_binding (binding_set, GDK_KEY_Down, modifiers, GTK_DIR_DOWN);
+
+  modifiers = 0;
+  add_move_binding (binding_set, GDK_KEY_KP_Down, modifiers, GTK_DIR_DOWN);
+
+  modifiers = 0;
+  add_move_binding (binding_set, GDK_KEY_Left, modifiers, GTK_DIR_LEFT);
+
+  modifiers = 0;
+  add_move_binding (binding_set, GDK_KEY_KP_Left, modifiers, GTK_DIR_LEFT);
+
+  modifiers = 0;
+  add_move_binding (binding_set, GDK_KEY_Right, modifiers, GTK_DIR_RIGHT);
+
+  modifiers = 0;
+  add_move_binding (binding_set, GDK_KEY_KP_Right, modifiers, GTK_DIR_RIGHT);
 }
 
 static void
@@ -2167,6 +2565,8 @@ gf_icon_view_init (GfIconView *self)
   g_signal_connect (self, "select-all", G_CALLBACK (select_all_cb), NULL);
   g_signal_connect (self, "unselect-all", G_CALLBACK (unselect_all_cb), NULL);
   g_signal_connect (self, "activate", G_CALLBACK (activate_cb), NULL);
+  g_signal_connect (self, "toggle", G_CALLBACK (toggle_cb), NULL);
+  g_signal_connect (self, "move", G_CALLBACK (move_cb), NULL);
 
   add_event_filter (self);
 
diff --git a/gnome-flashback/libdesktop/gf-icon.c b/gnome-flashback/libdesktop/gf-icon.c
index 9cd111e..d29d10f 100644
--- a/gnome-flashback/libdesktop/gf-icon.c
+++ b/gnome-flashback/libdesktop/gf-icon.c
@@ -368,6 +368,8 @@ multi_press_pressed_cb (GtkGestureMultiPress *gesture,
   control_pressed = (state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK;
   shift_pressed = (state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK;
 
+  gtk_widget_grab_focus (GTK_WIDGET (priv->icon_view));
+
   if (button == GDK_BUTTON_PRIMARY)
     {
       if (!control_pressed && !shift_pressed)
@@ -772,6 +774,8 @@ gf_icon_init (GfIcon *self)
                     G_CALLBACK (multi_press_pressed_cb),
                     self);
 
+  gtk_widget_set_focus_on_click (GTK_WIDGET (self), FALSE);
+
   box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
   gtk_container_add (GTK_CONTAINER (self), box);
   gtk_widget_show (box);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]