Merge branch 'columnview-menu' into 'master'

columnview: Add header menus

See merge request GNOME/gtk!2001
This commit is contained in:
Matthias Clasen
2020-06-03 15:51:26 +00:00
6 changed files with 257 additions and 9 deletions

View File

@@ -300,7 +300,13 @@ do_listview_settings (GtkWidget *do_widget)
GtkBuilderScope *scope; GtkBuilderScope *scope;
GtkBuilder *builder; GtkBuilder *builder;
GtkColumnViewColumn *name_column; GtkColumnViewColumn *name_column;
GtkColumnViewColumn *type_column;
GtkColumnViewColumn *default_column;
GtkColumnViewColumn *summary_column;
GtkColumnViewColumn *description_column;
GtkSorter *sorter; GtkSorter *sorter;
GActionGroup *actions;
GAction *action;
g_type_ensure (SETTINGS_TYPE_KEY); g_type_ensure (SETTINGS_TYPE_KEY);
@@ -320,6 +326,32 @@ do_listview_settings (GtkWidget *do_widget)
listview = GTK_WIDGET (gtk_builder_get_object (builder, "listview")); listview = GTK_WIDGET (gtk_builder_get_object (builder, "listview"));
columnview = GTK_WIDGET (gtk_builder_get_object (builder, "columnview")); 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 ());
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);
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);
model = create_settings_model (NULL, NULL); model = create_settings_model (NULL, NULL);
treemodel = gtk_tree_list_model_new (FALSE, treemodel = gtk_tree_list_model_new (FALSE,
model, model,

View File

@@ -77,6 +77,7 @@
<child> <child>
<object class="GtkColumnViewColumn" id="name_column"> <object class="GtkColumnViewColumn" id="name_column">
<property name="title">Name</property> <property name="title">Name</property>
<property name="header-menu">header_menu</property>
<property name="factory"> <property name="factory">
<object class="GtkBuilderListItemFactory"> <object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[ <property name="bytes"><![CDATA[
@@ -103,6 +104,7 @@
<child> <child>
<object class="GtkColumnViewColumn"> <object class="GtkColumnViewColumn">
<property name="title">Value</property> <property name="title">Value</property>
<property name="header-menu">header_menu</property>
<property name="factory"> <property name="factory">
<object class="GtkBuilderListItemFactory"> <object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[ <property name="bytes"><![CDATA[
@@ -127,8 +129,9 @@
</object> </object>
</child> </child>
<child> <child>
<object class="GtkColumnViewColumn"> <object class="GtkColumnViewColumn" id="type_column">
<property name="title">Type</property> <property name="title">Type</property>
<property name="header-menu">header_menu</property>
<property name="factory"> <property name="factory">
<object class="GtkBuilderListItemFactory"> <object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[ <property name="bytes"><![CDATA[
@@ -153,8 +156,9 @@
</object> </object>
</child> </child>
<child> <child>
<object class="GtkColumnViewColumn"> <object class="GtkColumnViewColumn" id="default_column">
<property name="title">Default</property> <property name="title">Default</property>
<property name="header-menu">header_menu</property>
<property name="factory"> <property name="factory">
<object class="GtkBuilderListItemFactory"> <object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[ <property name="bytes"><![CDATA[
@@ -172,6 +176,68 @@
</object> </object>
</property> </property>
</template> </template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn" id="summary_column">
<property name="title">Summary</property>
<property name="visible">0</property>
<property name="header-menu">header_menu</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="xalign">0</property>
<property name="wrap">1</property>
<property name="width-chars">50</property>
<property name="max-width-chars">50</property>
<binding name="label">
<lookup name="summary" type="SettingsKey">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn" id="description_column">
<property name="title">Description</property>
<property name="visible">0</property>
<property name="header-menu">header_menu</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="xalign">0</property>
<property name="wrap">1</property>
<property name="width-chars">50</property>
<property name="max-width-chars">50</property>
<binding name="label">
<lookup name="description" type="SettingsKey">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface> </interface>
]]></property> ]]></property>
</object> </object>
@@ -187,4 +253,24 @@
</object> </object>
</child> </child>
</object> </object>
<menu id="header_menu">
<section>
<item>
<attribute name="label" translatable="yes">Type</attribute>
<attribute name="action">columnview.show-type</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Default value</attribute>
<attribute name="action">columnview.show-default</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Summary</attribute>
<attribute name="action">columnview.show-summary</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Description</attribute>
<attribute name="action">columnview.show-description</attribute>
</item>
</section>
</menu>
</interface> </interface>

View File

@@ -533,6 +533,9 @@ gtk_column_view_column_set_sorter
gtk_column_view_column_get_sorter gtk_column_view_column_get_sorter
gtk_column_view_column_set_visible gtk_column_view_column_set_visible
gtk_column_view_column_get_visible gtk_column_view_column_get_visible
gtk_column_view_column_set_header_menu
gtk_column_view_column_get_header_menu
<SUBSECTION Standard> <SUBSECTION Standard>
GTK_COLUMN_VIEW_COLUMN GTK_COLUMN_VIEW_COLUMN
GTK_COLUMN_VIEW_COLUMN_CLASS GTK_COLUMN_VIEW_COLUMN_CLASS

View File

@@ -63,6 +63,8 @@ struct _GtkColumnViewColumn
gboolean visible; gboolean visible;
GMenuModel *menu;
/* This list isn't sorted - this is just caching for performance */ /* This list isn't sorted - this is just caching for performance */
GtkColumnViewCell *first_cell; /* no reference, just caching */ GtkColumnViewCell *first_cell; /* no reference, just caching */
}; };
@@ -80,6 +82,7 @@ enum
PROP_TITLE, PROP_TITLE,
PROP_SORTER, PROP_SORTER,
PROP_VISIBLE, PROP_VISIBLE,
PROP_HEADER_MENU,
N_PROPS N_PROPS
}; };
@@ -99,6 +102,7 @@ gtk_column_view_column_dispose (GObject *object)
g_clear_object (&self->factory); g_clear_object (&self->factory);
g_clear_object (&self->sorter); g_clear_object (&self->sorter);
g_clear_pointer (&self->title, g_free); g_clear_pointer (&self->title, g_free);
g_clear_object (&self->menu);
G_OBJECT_CLASS (gtk_column_view_column_parent_class)->dispose (object); 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); g_value_set_boolean (value, self->visible);
break; break;
case PROP_HEADER_MENU:
g_value_set_object (value, self->menu);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break; 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)); gtk_column_view_column_set_visible (self, g_value_get_boolean (value));
break; break;
case PROP_HEADER_MENU:
gtk_column_view_column_set_header_menu (self, g_value_get_object (value));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break; break;
@@ -240,6 +252,18 @@ gtk_column_view_column_class_init (GtkColumnViewColumnClass *klass)
TRUE, TRUE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); 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); 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; 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;
}

View File

@@ -78,6 +78,12 @@ void gtk_column_view_column_set_visible (GtkColu
GDK_AVAILABLE_IN_ALL GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_column_get_visible (GtkColumnViewColumn *self); 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 G_END_DECLS
#endif /* __GTK_COLUMN_VIEW_COLUMN_H__ */ #endif /* __GTK_COLUMN_VIEW_COLUMN_H__ */

View File

@@ -30,6 +30,8 @@
#include "gtkbox.h" #include "gtkbox.h"
#include "gtkimage.h" #include "gtkimage.h"
#include "gtkgestureclick.h" #include "gtkgestureclick.h"
#include "gtkpopovermenu.h"
#include "gtknative.h"
struct _GtkColumnViewTitle struct _GtkColumnViewTitle
{ {
@@ -40,6 +42,7 @@ struct _GtkColumnViewTitle
GtkWidget *box; GtkWidget *box;
GtkWidget *title; GtkWidget *title;
GtkWidget *sort; GtkWidget *sort;
GtkWidget *popup_menu;
}; };
struct _GtkColumnViewTitleClass struct _GtkColumnViewTitleClass
@@ -70,10 +73,14 @@ gtk_column_view_title_size_allocate (GtkWidget *widget,
int height, int height,
int baseline) int baseline)
{ {
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
GtkWidget *child = gtk_widget_get_first_child (widget); GtkWidget *child = gtk_widget_get_first_child (widget);
if (child) if (child)
gtk_widget_allocate (child, width, height, baseline, NULL); gtk_widget_allocate (child, width, height, baseline, NULL);
if (self->popup_menu)
gtk_native_check_resize (GTK_NATIVE (self->popup_menu));
} }
static void static void
@@ -82,6 +89,7 @@ gtk_column_view_title_dispose (GObject *object)
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (object); GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (object);
g_clear_pointer (&self->box, gtk_widget_unparent); g_clear_pointer (&self->box, gtk_widget_unparent);
g_clear_pointer (&self->popup_menu, gtk_widget_unparent);
g_clear_object (&self->column); g_clear_object (&self->column);
@@ -112,13 +120,8 @@ gtk_column_view_title_resize_func (GtkWidget *widget)
} }
static void static void
click_pressed_cb (GtkGestureClick *gesture, activate_sort (GtkColumnViewTitle *self)
guint n_press,
gdouble x,
gdouble y,
GtkWidget *widget)
{ {
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
GtkSorter *sorter; GtkSorter *sorter;
GtkColumnView *view; GtkColumnView *view;
GtkColumnViewSorter *view_sorter; GtkColumnViewSorter *view_sorter;
@@ -132,6 +135,56 @@ click_pressed_cb (GtkGestureClick *gesture,
gtk_column_view_sorter_add_column (view_sorter, self->column); 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 static void
gtk_column_view_title_init (GtkColumnViewTitle *self) 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); gtk_box_append (GTK_BOX (self->box), self->sort);
gesture = gtk_gesture_click_new (); 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)); gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
} }
@@ -199,6 +253,8 @@ gtk_column_view_title_update (GtkColumnViewTitle *self)
} }
else else
gtk_widget_hide (self->sort); gtk_widget_hide (self->sort);
g_clear_pointer (&self->popup_menu, gtk_widget_unparent);
} }
GtkColumnViewColumn * GtkColumnViewColumn *