From f27bd061fbfd0b69bd1110e1c380b017057519a3 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 21 Dec 2019 22:20:42 -0500 Subject: [PATCH 1/4] columnviewcolumn: Add a menu property Add a ::header-menu property that will be used to create a context menu for the header of the column. --- docs/reference/gtk/gtk4-sections.txt | 3 ++ gtk/gtkcolumnviewcolumn.c | 65 ++++++++++++++++++++++++++++ gtk/gtkcolumnviewcolumn.h | 6 +++ 3 files changed, 74 insertions(+) diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index a2fb71fbfc..05011437d4 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -533,6 +533,9 @@ gtk_column_view_column_set_sorter gtk_column_view_column_get_sorter gtk_column_view_column_set_visible gtk_column_view_column_get_visible +gtk_column_view_column_set_header_menu +gtk_column_view_column_get_header_menu + GTK_COLUMN_VIEW_COLUMN GTK_COLUMN_VIEW_COLUMN_CLASS diff --git a/gtk/gtkcolumnviewcolumn.c b/gtk/gtkcolumnviewcolumn.c index 5b61633d7a..d01cf0344e 100644 --- a/gtk/gtkcolumnviewcolumn.c +++ b/gtk/gtkcolumnviewcolumn.c @@ -63,6 +63,8 @@ struct _GtkColumnViewColumn gboolean visible; + GMenuModel *menu; + /* This list isn't sorted - this is just caching for performance */ GtkColumnViewCell *first_cell; /* no reference, just caching */ }; @@ -80,6 +82,7 @@ enum PROP_TITLE, PROP_SORTER, PROP_VISIBLE, + PROP_HEADER_MENU, N_PROPS }; @@ -99,6 +102,7 @@ gtk_column_view_column_dispose (GObject *object) g_clear_object (&self->factory); g_clear_object (&self->sorter); g_clear_pointer (&self->title, g_free); + g_clear_object (&self->menu); G_OBJECT_CLASS (gtk_column_view_column_parent_class)->dispose (object); } @@ -133,6 +137,10 @@ gtk_column_view_column_get_property (GObject *object, g_value_set_boolean (value, self->visible); break; + case PROP_HEADER_MENU: + g_value_set_object (value, self->menu); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -165,6 +173,10 @@ gtk_column_view_column_set_property (GObject *object, gtk_column_view_column_set_visible (self, g_value_get_boolean (value)); break; + case PROP_HEADER_MENU: + gtk_column_view_column_set_header_menu (self, g_value_get_object (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -240,6 +252,18 @@ gtk_column_view_column_class_init (GtkColumnViewColumnClass *klass) TRUE, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + /** + * GtkColumnViewColumn:header-menu: + * + * Menu model used to create the context menu for the column header. + */ + properties[PROP_HEADER_MENU] = + g_param_spec_object ("header-menu", + P_("Header menu"), + P_("Menu to use on the title of this column"), + G_TYPE_MENU_MODEL, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (gobject_class, N_PROPS, properties); } @@ -723,3 +747,44 @@ gtk_column_view_column_get_visible (GtkColumnViewColumn *self) return self->visible; } + +/** + * gtk_column_view_column_set_header_menu: + * @self: a #GtkColumnViewColumn + * @menu: (allow-none): a #GMenuModel, or %NULL + * + * Sets the menu model that is used to create the context menu + * for the column header. + */ +void +gtk_column_view_column_set_header_menu (GtkColumnViewColumn *self, + GMenuModel *menu) +{ + g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self)); + g_return_if_fail (menu == NULL || G_IS_MENU_MODEL (menu)); + + if (!g_set_object (&self->menu, menu)) + return; + + if (self->header) + gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header)); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HEADER_MENU]); +} + +/** + * gtk_column_view_column_get_header_menu: + * @self: a #GtkColumnViewColumn + * + * Gets the menu model that is used to create the context menu + * for the column header. + * + * Returns: the #GMenuModel, or %NULL + */ +GMenuModel * +gtk_column_view_column_get_header_menu (GtkColumnViewColumn *self) +{ + g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL); + + return self->menu; +} diff --git a/gtk/gtkcolumnviewcolumn.h b/gtk/gtkcolumnviewcolumn.h index f0005809be..dbe27c48f6 100644 --- a/gtk/gtkcolumnviewcolumn.h +++ b/gtk/gtkcolumnviewcolumn.h @@ -78,6 +78,12 @@ void gtk_column_view_column_set_visible (GtkColu GDK_AVAILABLE_IN_ALL gboolean gtk_column_view_column_get_visible (GtkColumnViewColumn *self); +GDK_AVAILABLE_IN_ALL +void gtk_column_view_column_set_header_menu (GtkColumnViewColumn *self, + GMenuModel *menu); +GDK_AVAILABLE_IN_ALL +GMenuModel * gtk_column_view_column_get_header_menu (GtkColumnViewColumn *self); + G_END_DECLS #endif /* __GTK_COLUMN_VIEW_COLUMN_H__ */ From cb2901ec4093ff1e75a389308a807713ec36d33c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 21 Dec 2019 22:21:27 -0500 Subject: [PATCH 2/4] columnviewtitle: Display a context menu When the ::header-menu property is set on the column, use the menu model to create and show a context menu. --- gtk/gtkcolumnviewtitle.c | 70 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/gtk/gtkcolumnviewtitle.c b/gtk/gtkcolumnviewtitle.c index 306064bcd8..ae66022ac9 100644 --- a/gtk/gtkcolumnviewtitle.c +++ b/gtk/gtkcolumnviewtitle.c @@ -30,6 +30,8 @@ #include "gtkbox.h" #include "gtkimage.h" #include "gtkgestureclick.h" +#include "gtkpopovermenu.h" +#include "gtknative.h" struct _GtkColumnViewTitle { @@ -40,6 +42,7 @@ struct _GtkColumnViewTitle GtkWidget *box; GtkWidget *title; GtkWidget *sort; + GtkWidget *popup_menu; }; struct _GtkColumnViewTitleClass @@ -70,10 +73,14 @@ gtk_column_view_title_size_allocate (GtkWidget *widget, int height, int baseline) { + GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget); GtkWidget *child = gtk_widget_get_first_child (widget); if (child) gtk_widget_allocate (child, width, height, baseline, NULL); + + if (self->popup_menu) + gtk_native_check_resize (GTK_NATIVE (self->popup_menu)); } static void @@ -82,6 +89,7 @@ gtk_column_view_title_dispose (GObject *object) GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (object); g_clear_pointer (&self->box, gtk_widget_unparent); + g_clear_pointer (&self->popup_menu, gtk_widget_unparent); g_clear_object (&self->column); @@ -112,13 +120,8 @@ gtk_column_view_title_resize_func (GtkWidget *widget) } static void -click_pressed_cb (GtkGestureClick *gesture, - guint n_press, - gdouble x, - gdouble y, - GtkWidget *widget) +activate_sort (GtkColumnViewTitle *self) { - GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget); GtkSorter *sorter; GtkColumnView *view; GtkColumnViewSorter *view_sorter; @@ -132,6 +135,56 @@ click_pressed_cb (GtkGestureClick *gesture, gtk_column_view_sorter_add_column (view_sorter, self->column); } +static void +show_menu (GtkColumnViewTitle *self, + double x, + double y) +{ + if (!self->popup_menu) + { + GMenuModel *model; + + model = gtk_column_view_column_get_header_menu (self->column); + if (!model) + return; + + self->popup_menu = gtk_popover_menu_new_from_model (model); + gtk_widget_set_parent (self->popup_menu, GTK_WIDGET (self)); + gtk_popover_set_position (GTK_POPOVER (self->popup_menu), GTK_POS_BOTTOM); + + gtk_popover_set_has_arrow (GTK_POPOVER (self->popup_menu), FALSE); + gtk_widget_set_halign (self->popup_menu, GTK_ALIGN_START); + } + + if (x != -1 && y != -1) + { + GdkRectangle rect = { x, y, 1, 1 }; + gtk_popover_set_pointing_to (GTK_POPOVER (self->popup_menu), &rect); + } + else + gtk_popover_set_pointing_to (GTK_POPOVER (self->popup_menu), NULL); + + gtk_popover_popup (GTK_POPOVER (self->popup_menu)); +} + +static void +click_released_cb (GtkGestureClick *gesture, + guint n_press, + double x, + double y, + GtkWidget *widget) +{ + GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget); + guint button; + + button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); + + if (button == GDK_BUTTON_PRIMARY) + activate_sort (self); + else if (button == GDK_BUTTON_SECONDARY) + show_menu (self, x, y); +} + static void gtk_column_view_title_init (GtkColumnViewTitle *self) { @@ -150,7 +203,8 @@ gtk_column_view_title_init (GtkColumnViewTitle *self) gtk_box_append (GTK_BOX (self->box), self->sort); gesture = gtk_gesture_click_new (); - g_signal_connect (gesture, "pressed", G_CALLBACK (click_pressed_cb), self); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0); + g_signal_connect (gesture, "released", G_CALLBACK (click_released_cb), self); gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture)); } @@ -199,6 +253,8 @@ gtk_column_view_title_update (GtkColumnViewTitle *self) } else gtk_widget_hide (self->sort); + + g_clear_pointer (&self->popup_menu, gtk_widget_unparent); } GtkColumnViewColumn * From c835214540cd4b13ef33ee36d1fb049798502c44 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 31 May 2020 14:44:42 -0400 Subject: [PATCH 3/4] Add a header menu to settings demo Allow toggling column visibility from here. --- demos/gtk-demo/listview_settings.c | 20 ++++++++++++++++++++ demos/gtk-demo/listview_settings.ui | 20 ++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/demos/gtk-demo/listview_settings.c b/demos/gtk-demo/listview_settings.c index c00024a68e..24c42ea232 100644 --- a/demos/gtk-demo/listview_settings.c +++ b/demos/gtk-demo/listview_settings.c @@ -300,7 +300,11 @@ do_listview_settings (GtkWidget *do_widget) GtkBuilderScope *scope; GtkBuilder *builder; GtkColumnViewColumn *name_column; + GtkColumnViewColumn *type_column; + GtkColumnViewColumn *default_column; GtkSorter *sorter; + GActionGroup *actions; + GAction *action; g_type_ensure (SETTINGS_TYPE_KEY); @@ -320,6 +324,22 @@ do_listview_settings (GtkWidget *do_widget) listview = GTK_WIDGET (gtk_builder_get_object (builder, "listview")); columnview = GTK_WIDGET (gtk_builder_get_object (builder, "columnview")); + type_column = GTK_COLUMN_VIEW_COLUMN (gtk_builder_get_object (builder, "type_column")); + default_column = GTK_COLUMN_VIEW_COLUMN (gtk_builder_get_object (builder, "default_column")); + + actions = G_ACTION_GROUP (g_simple_action_group_new ()); + + action = G_ACTION (g_property_action_new ("show-type", type_column, "visible")); + g_action_map_add_action (G_ACTION_MAP (actions), action); + g_object_unref (action); + + action = G_ACTION (g_property_action_new ("show-default", default_column, "visible")); + g_action_map_add_action (G_ACTION_MAP (actions), action); + g_object_unref (action); + + gtk_widget_insert_action_group (columnview, "columnview", actions); + g_object_unref (actions); + model = create_settings_model (NULL, NULL); treemodel = gtk_tree_list_model_new (FALSE, model, diff --git a/demos/gtk-demo/listview_settings.ui b/demos/gtk-demo/listview_settings.ui index cc9f7f8a9c..96d2a22cfe 100644 --- a/demos/gtk-demo/listview_settings.ui +++ b/demos/gtk-demo/listview_settings.ui @@ -77,6 +77,7 @@ Name + header_menu Value + header_menu - + Type + header_menu - + Default + header_menu + +
+ + Type + columnview.show-type + + + Default value + columnview.show-default + +
+
From d02c48f987949ecdbcddb4a1a938089f0b50f06c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 1 Jun 2020 14:46:12 -0400 Subject: [PATCH 4/4] Add more columns to the settings demo Flesh out this demo more, so we can have a non-trivial column editor here at some point. --- demos/gtk-demo/listview_settings.c | 12 +++++ demos/gtk-demo/listview_settings.ui | 70 +++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/demos/gtk-demo/listview_settings.c b/demos/gtk-demo/listview_settings.c index 24c42ea232..5bb7a2b8cc 100644 --- a/demos/gtk-demo/listview_settings.c +++ b/demos/gtk-demo/listview_settings.c @@ -302,6 +302,8 @@ do_listview_settings (GtkWidget *do_widget) GtkColumnViewColumn *name_column; GtkColumnViewColumn *type_column; GtkColumnViewColumn *default_column; + GtkColumnViewColumn *summary_column; + GtkColumnViewColumn *description_column; GtkSorter *sorter; GActionGroup *actions; GAction *action; @@ -326,6 +328,8 @@ do_listview_settings (GtkWidget *do_widget) columnview = GTK_WIDGET (gtk_builder_get_object (builder, "columnview")); type_column = GTK_COLUMN_VIEW_COLUMN (gtk_builder_get_object (builder, "type_column")); default_column = GTK_COLUMN_VIEW_COLUMN (gtk_builder_get_object (builder, "default_column")); + summary_column = GTK_COLUMN_VIEW_COLUMN (gtk_builder_get_object (builder, "summary_column")); + description_column = GTK_COLUMN_VIEW_COLUMN (gtk_builder_get_object (builder, "description_column")); actions = G_ACTION_GROUP (g_simple_action_group_new ()); @@ -337,6 +341,14 @@ do_listview_settings (GtkWidget *do_widget) g_action_map_add_action (G_ACTION_MAP (actions), action); g_object_unref (action); + action = G_ACTION (g_property_action_new ("show-summary", summary_column, "visible")); + g_action_map_add_action (G_ACTION_MAP (actions), action); + g_object_unref (action); + + action = G_ACTION (g_property_action_new ("show-description", description_column, "visible")); + g_action_map_add_action (G_ACTION_MAP (actions), action); + g_object_unref (action); + gtk_widget_insert_action_group (columnview, "columnview", actions); g_object_unref (actions); diff --git a/demos/gtk-demo/listview_settings.ui b/demos/gtk-demo/listview_settings.ui index 96d2a22cfe..d567803140 100644 --- a/demos/gtk-demo/listview_settings.ui +++ b/demos/gtk-demo/listview_settings.ui @@ -176,6 +176,68 @@
+ + ]]> +
+
+
+ + + + Summary + 0 + header_menu + + + + + + + ]]> + + + + + + + Description + 0 + header_menu + + + + + ]]> @@ -201,6 +263,14 @@ Default value columnview.show-default + + Summary + columnview.show-summary + + + Description + columnview.show-description +