[nautilus/antonioffix-menus-and-popovers: 1/7] files-view: Pop up context menu near selection



commit 29cf64bd7632b25dd56a275078da6130b201edeb
Author: António Fernandes <antoniof gnome org>
Date:   Sun Jan 7 02:00:05 2018 +0000

    files-view: Pop up context menu near selection
    
    Context menus pop up where the pointer is, which is at the
    selection if the pointing device is used to open the menu.
    
    However, if the <Menu> key or <Shift>+<F10> keyboard shortcut
    are used, context menus still shows up wherever the pointer is,
    which may not be near the selected item at all.
    
    Instead, when the context menu is activated from the keyboard,
    show it next to the selected item. If multiple items are selected,
    show it next to the focused item. If the focus is not on a selected
    item, show it next to the selected item which is sorted the lowest.
    In all cases, scroll as necessary to make sure the whole item is
    visible.
    
    Fixes: https://bugzilla.gnome.org/show_bug.cgi?id=102666

 src/nautilus-canvas-container.c     | 41 +++++++++++++++++++++++
 src/nautilus-canvas-container.h     |  3 ++
 src/nautilus-canvas-view.c          | 48 ++++++++++++++++++++++++++
 src/nautilus-files-view.c           | 35 +++++++++++++++++--
 src/nautilus-files-view.h           |  2 ++
 src/nautilus-list-view.c            | 67 +++++++++++++++++++++++++++++++++++++
 src/nautilus-view-icon-controller.c | 50 +++++++++++++++++++++++++++
 7 files changed, 243 insertions(+), 3 deletions(-)
---
diff --git a/src/nautilus-canvas-container.c b/src/nautilus-canvas-container.c
index f42d56d60..5372df654 100644
--- a/src/nautilus-canvas-container.c
+++ b/src/nautilus-canvas-container.c
@@ -4594,6 +4594,21 @@ nautilus_canvas_container_get_first_visible_icon (NautilusCanvasContainer *conta
     return best_icon ? best_icon->data : NULL;
 }
 
+NautilusCanvasIconData *
+nautilus_canvas_container_get_focused_icon (NautilusCanvasContainer *container)
+{
+    NautilusCanvasIcon *icon;
+
+    icon = container->details->focus;
+
+    if (icon != NULL)
+    {
+        return icon->data;
+    }
+
+    return NULL;
+}
+
 /* puts the icon at the top of the screen */
 void
 nautilus_canvas_container_scroll_to_canvas (NautilusCanvasContainer *container,
@@ -5330,6 +5345,32 @@ nautilus_canvas_container_get_icon_locations (NautilusCanvasContainer *container
     return result;
 }
 
+/* Returns a GdkRectangle of the icon. The bounding box is adjusted with the
+ * pixels_per_unit already, so they are the final positions on the canvas */
+GdkRectangle *
+nautilus_canvas_container_get_icon_bounding_box (NautilusCanvasContainer *container,
+                                                 NautilusCanvasIconData  *data)
+{
+    NautilusCanvasIcon *icon;
+    int x1, x2, y1, y2;
+    GdkRectangle *bounding_box;
+
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), NULL);
+    g_return_val_if_fail (data != NULL, NULL);
+
+    icon = g_hash_table_lookup (container->details->icon_set, data);
+    icon_get_bounding_box (icon,
+                           &x1, &y1, &x2, &y2,
+                           BOUNDS_USAGE_FOR_DISPLAY);
+    bounding_box = g_malloc0 (sizeof (GdkRectangle));
+    bounding_box->x = x1 * EEL_CANVAS (container)->pixels_per_unit;
+    bounding_box->width = (x2 - x1) * EEL_CANVAS (container)->pixels_per_unit;
+    bounding_box->y = y1 * EEL_CANVAS (container)->pixels_per_unit;
+    bounding_box->height = (y2 - y1) * EEL_CANVAS (container)->pixels_per_unit;
+
+    return bounding_box;
+}
+
 /* Returns an array of GdkRectangles of the icons. The bounding box is adjusted
  * with the pixels_per_unit already, so they are the final positions on the canvas */
 static GArray *
diff --git a/src/nautilus-canvas-container.h b/src/nautilus-canvas-container.h
index 63cec4b6b..9da9fa61f 100644
--- a/src/nautilus-canvas-container.h
+++ b/src/nautilus-canvas-container.h
@@ -226,6 +226,9 @@ void              nautilus_canvas_container_reveal                        (Nauti
                                                                           NautilusCanvasIconData       
*data);
 gboolean          nautilus_canvas_container_is_empty                      (NautilusCanvasContainer  
*container);
 NautilusCanvasIconData *nautilus_canvas_container_get_first_visible_icon        (NautilusCanvasContainer  
*container);
+NautilusCanvasIconData *nautilus_canvas_container_get_focused_icon              (NautilusCanvasContainer  
*container);
+GdkRectangle      *nautilus_canvas_container_get_icon_bounding_box          (NautilusCanvasContainer  
*container,
+                                                                            NautilusCanvasIconData       
*data);
 void              nautilus_canvas_container_scroll_to_canvas                (NautilusCanvasContainer  
*container,
                                                                             NautilusCanvasIconData       
*data);
 
diff --git a/src/nautilus-canvas-view.c b/src/nautilus-canvas-view.c
index f00fa5064..2990a2923 100644
--- a/src/nautilus-canvas-view.c
+++ b/src/nautilus-canvas-view.c
@@ -959,6 +959,53 @@ nautilus_canvas_view_compute_rename_popover_pointing_to (NautilusFilesView *view
     return bounding_box;
 }
 
+static GdkRectangle *
+nautilus_canvas_view_reveal_for_selection_context_menu (NautilusFilesView *view)
+{
+    g_autoptr (GList) selection = NULL;
+    NautilusCanvasContainer *container;
+    NautilusCanvasIconData *data;
+    GdkRectangle *rectangle;
+
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), NULL);
+
+    selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
+    g_return_val_if_fail (selection != NULL, NULL);
+
+    container = get_canvas_container (NAUTILUS_CANVAS_VIEW (view));
+
+    /* Update the icon ordering to reveal the rigth selection */
+    nautilus_canvas_container_layout_now (container);
+
+    /* Get the data of the focused item, if selected. Otherwise, get the
+     * data of the last selected item.*/
+    data = nautilus_canvas_container_get_focused_icon (container);
+    if (data == NULL || g_list_find (selection, NAUTILUS_FILE (data)) == NULL)
+    {
+        selection = g_list_last (selection);
+        data = NAUTILUS_CANVAS_ICON_DATA (selection->data);
+    }
+
+    nautilus_canvas_container_reveal (container, data);
+
+    rectangle = nautilus_canvas_container_get_icon_bounding_box (container, data);
+    if (rectangle != NULL)
+    {
+        GtkScrolledWindow *parent_container;
+        GtkAdjustment *vadjustment;
+        GtkAdjustment *hadjustment;
+
+        parent_container = GTK_SCROLLED_WINDOW (nautilus_files_view_get_content_widget (view));
+        vadjustment = gtk_scrolled_window_get_vadjustment (parent_container);
+        hadjustment = gtk_scrolled_window_get_hadjustment (parent_container);
+
+        rectangle->x -= gtk_adjustment_get_value (hadjustment);
+        rectangle->y -= gtk_adjustment_get_value (vadjustment);
+    }
+
+    return rectangle;
+}
+
 static void
 nautilus_canvas_view_set_selection (NautilusFilesView *view,
                                     GList             *selection)
@@ -1515,6 +1562,7 @@ nautilus_canvas_view_class_init (NautilusCanvasViewClass *klass)
     nautilus_files_view_class->get_view_id = nautilus_canvas_view_get_id;
     nautilus_files_view_class->get_first_visible_file = canvas_view_get_first_visible_file;
     nautilus_files_view_class->scroll_to_file = canvas_view_scroll_to_file;
+    nautilus_files_view_class->reveal_for_selection_context_menu = 
nautilus_canvas_view_reveal_for_selection_context_menu;
 }
 
 static void
diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c
index 97ef75fed..639fd000f 100644
--- a/src/nautilus-files-view.c
+++ b/src/nautilus-files-view.c
@@ -8106,6 +8106,12 @@ nautilus_files_view_update_toolbar_menus (NautilusFilesView *view)
     nautilus_files_view_reset_view_menu (view);
 }
 
+static GdkRectangle *
+nautilus_files_view_reveal_for_selection_context_menu (NautilusFilesView *view)
+{
+    return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->reveal_for_selection_context_menu (view);
+}
+
 /**
  * nautilus_files_view_pop_up_selection_context_menu
  *
@@ -8129,9 +8135,32 @@ nautilus_files_view_pop_up_selection_context_menu  (NautilusFilesView *view,
      */
     update_context_menus_if_pending (view);
 
-    nautilus_pop_up_context_menu_at_pointer (GTK_WIDGET (view),
-                                             priv->selection_menu,
-                                             event);
+    if (event != NULL)
+    {
+        nautilus_pop_up_context_menu_at_pointer (GTK_WIDGET (view),
+                                                 priv->selection_menu,
+                                                 event);
+    }
+    else
+    {
+        /* If triggered from the keyboard, popup at selection, not pointer */
+        g_autofree GdkRectangle *rectangle = NULL;
+        g_autoptr (GtkWidget) gtk_menu = NULL;
+
+        rectangle = nautilus_files_view_reveal_for_selection_context_menu (view);
+        g_return_if_fail (rectangle != NULL);
+
+        gtk_menu = gtk_menu_new_from_model (G_MENU_MODEL (priv->selection_menu));
+        gtk_menu_attach_to_widget (GTK_MENU (gtk_menu), GTK_WIDGET (view), NULL);
+
+        gtk_menu_popup_at_rect (GTK_MENU (gtk_menu),
+                                gtk_widget_get_window (GTK_WIDGET (view)),
+                                rectangle,
+                                GDK_GRAVITY_SOUTH_WEST,
+                                GDK_GRAVITY_NORTH_WEST,
+                                NULL);
+        g_object_ref_sink (gtk_menu);
+    }
 }
 
 /**
diff --git a/src/nautilus-files-view.h b/src/nautilus-files-view.h
index 80384f27d..c4c1d19d7 100644
--- a/src/nautilus-files-view.h
+++ b/src/nautilus-files-view.h
@@ -231,6 +231,8 @@ struct _NautilusFilesViewClass {
 
         GdkRectangle * (* compute_rename_popover_pointing_to) (NautilusFilesView *view);
 
+        GdkRectangle * (* reveal_for_selection_context_menu) (NautilusFilesView *view);
+
         GIcon *        (* get_icon)          (NautilusFilesView *view);
 
         /* Use this to show an optional visual feedback when the directory is empty.
diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c
index 90d2ae690..4f01b567b 100644
--- a/src/nautilus-list-view.c
+++ b/src/nautilus-list-view.c
@@ -3695,6 +3695,72 @@ nautilus_list_view_compute_rename_popover_pointing_to (NautilusFilesView *view)
     return rect;
 }
 
+static GdkRectangle *
+nautilus_list_view_reveal_for_selection_context_menu (NautilusFilesView *view)
+{
+    NautilusListView *list_view;
+    GtkTreeView *tree_view;
+    GtkTreeSelection *tree_selection;
+    GtkTreePath *path;
+    GdkRectangle *rect;
+    int header_height;
+
+    g_return_val_if_fail (NAUTILUS_IS_LIST_VIEW (view), NULL);
+
+    list_view = NAUTILUS_LIST_VIEW (view);
+    tree_view = list_view->details->tree_view;
+    tree_selection = gtk_tree_view_get_selection (tree_view);
+    g_return_val_if_fail (tree_selection != NULL, NULL);
+
+    /* Get the path to the last focused item, if selected. Otherwise, get
+     * the path to the selected item which is sorted the lowest.
+     */
+    gtk_tree_view_get_cursor (tree_view, &path, NULL);
+    if (path == NULL || !gtk_tree_selection_path_is_selected (tree_selection, path))
+    {
+        GList *list;
+
+        list = gtk_tree_selection_get_selected_rows (tree_selection, NULL);
+        list = g_list_last (list);
+        path = g_steal_pointer(&list->data);
+
+        g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
+    }
+
+    gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
+
+    rect = g_malloc0 (sizeof (GdkRectangle));
+    gtk_tree_view_get_cell_area (tree_view,
+                                 path,
+                                 list_view->details->file_name_column,
+                                 rect);
+    gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
+                                                       rect->x, rect->y,
+                                                       &rect->x, &rect->y);
+
+    /* FIXME Due to smooth scrolling, we may get the cell area while the view is
+     * still scrolling (and still outside the view), not at the final position
+     * of the cell after scrolling.
+     * https://bugzilla.gnome.org/show_bug.cgi?id=746773
+     * The following workaround guesses the final "y" coordinate by clamping it
+     * to the widget edge. Note that the top edge has got columns header, which
+     * is private, so first guess the header height from the difference between
+     * widget coordinates and bin cooridinates.
+     */
+    gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
+                                                       0, 0,
+                                                       NULL, &header_height);
+
+    rect->y = CLAMP (rect->y,
+                     header_height,
+                     gtk_widget_get_allocated_height (GTK_WIDGET (list_view)) - rect->height);
+    /* End of workaround */
+
+    gtk_tree_path_free (path);
+
+    return rect;
+}
+
 static void
 nautilus_list_view_class_init (NautilusListViewClass *class)
 {
@@ -3734,6 +3800,7 @@ nautilus_list_view_class_init (NautilusListViewClass *class)
     nautilus_files_view_class->get_first_visible_file = nautilus_list_view_get_first_visible_file;
     nautilus_files_view_class->scroll_to_file = list_view_scroll_to_file;
     nautilus_files_view_class->compute_rename_popover_pointing_to = 
nautilus_list_view_compute_rename_popover_pointing_to;
+    nautilus_files_view_class->reveal_for_selection_context_menu = 
nautilus_list_view_reveal_for_selection_context_menu;
 }
 
 static void
diff --git a/src/nautilus-view-icon-controller.c b/src/nautilus-view-icon-controller.c
index 9d39e66a4..6e048b6ec 100644
--- a/src/nautilus-view-icon-controller.c
+++ b/src/nautilus-view-icon-controller.c
@@ -639,6 +639,55 @@ real_compute_rename_popover_pointing_to (NautilusFilesView *files_view)
     return allocation;
 }
 
+static GdkRectangle *
+real_reveal_for_selection_context_menu (NautilusFilesView *files_view)
+{
+    g_autoptr (GList) selection = NULL;
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+    GtkWidget *item_ui;
+    GdkRectangle *rectangle;
+    GtkWidget *content_widget;
+    GtkAdjustment *vadjustment;
+    int view_height;
+
+    selection = nautilus_view_get_selection (NAUTILUS_VIEW (files_view));
+    g_return_val_if_fail (selection != NULL, NULL);
+
+    /* Get the focused item_ui, if selected.
+     * Otherwise, get the selected item_ui which is sorted the lowest.*/
+    item_ui = gtk_container_get_focus_child (GTK_CONTAINER (self->view_ui));
+    if (item_ui == NULL || !gtk_flow_box_child_is_selected (GTK_FLOW_BOX_CHILD (item_ui)))
+    {
+        g_autoptr (GList) list = gtk_flow_box_get_selected_children (GTK_FLOW_BOX (self->view_ui));
+
+        list = g_list_last (list);
+        item_ui = GTK_WIDGET (list->data);
+    }
+
+    rectangle = g_malloc0 (sizeof (GdkRectangle));
+    gtk_widget_get_allocation (item_ui, (GtkAllocation *) rectangle);
+    content_widget = nautilus_files_view_get_content_widget (files_view);
+    view_height = gtk_widget_get_allocated_height (content_widget);
+    vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (content_widget));
+
+    /* Scroll only as necessary. TODO: Would be nice to have this as part of
+     * GtkFlowBox. GtkTreeView has something similar. */
+    if (rectangle->y < gtk_adjustment_get_value (vadjustment))
+    {
+        gtk_adjustment_set_value (vadjustment, rectangle->y);
+    }
+    else if (rectangle->y + rectangle->height >
+                gtk_adjustment_get_value (vadjustment) + view_height)
+    {
+        gtk_adjustment_set_value (vadjustment,
+                                  rectangle->y + rectangle->height - view_height);
+    }
+
+    rectangle->y -= gtk_adjustment_get_value (vadjustment);
+
+    return rectangle;
+}
+
 static void
 real_click_policy_changed (NautilusFilesView *files_view)
 {
@@ -930,6 +979,7 @@ nautilus_view_icon_controller_class_init (NautilusViewIconControllerClass *klass
     files_view_class->get_zoom_level_percentage = real_get_zoom_level_percentage;
     files_view_class->is_zoom_level_default = real_is_zoom_level_default;
     files_view_class->compute_rename_popover_pointing_to = real_compute_rename_popover_pointing_to;
+    files_view_class->reveal_for_selection_context_menu = real_reveal_for_selection_context_menu;
 }
 
 static void


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