diff options
author | Alexander Mikhaylenko <alexm@gnome.org> | 2021-09-03 16:18:06 +0500 |
---|---|---|
committer | Alexander Mikhaylenko <alexm@gnome.org> | 2021-09-03 17:52:46 +0500 |
commit | 8b48cf11f9d4fa2916291bfaaed1597b18e6c55e (patch) | |
tree | 365113e83f616cd5a7abffaa25a703d9ea406aaa | |
parent | e681fdd958751f02a68b472b5bde26086fdeb8cb (diff) | |
download | gtk+-8b48cf11f9d4fa2916291bfaaed1597b18e6c55e.tar.gz |
menubutton: Support custom children
Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/4205
-rw-r--r-- | gtk/gtkmenubutton.c | 169 | ||||
-rw-r--r-- | gtk/gtkmenubutton.h | 6 | ||||
-rw-r--r-- | gtk/theme/Default/_common.scss | 9 |
3 files changed, 175 insertions, 9 deletions
diff --git a/gtk/gtkmenubutton.c b/gtk/gtkmenubutton.c index e1d8209ddc..02eb13b4a0 100644 --- a/gtk/gtkmenubutton.c +++ b/gtk/gtkmenubutton.c @@ -66,8 +66,9 @@ * `GtkMenuButton` has a single CSS node with name `menubutton` * which contains a `button` node with a `.toggle` style class. * - * If the button contains only an icon or an arrow, it will have the - * `.image-button` style class, if it contains both, it will have the + * If the button contains an icon, it will have the `.image-button` style class, + * if it contains text, it will have `.text-button` style class. If an arrow is + * visible in addition to an icon, text or a custom child, it will also have * `.arrow-button` style class. * * Inside the toggle button content, there is an `arrow` node for @@ -87,6 +88,7 @@ #include "config.h" #include "gtkactionable.h" +#include "gtkbuildable.h" #include "gtkbuiltiniconprivate.h" #include "gtkintl.h" #include "gtkimage.h" @@ -122,6 +124,7 @@ struct _GtkMenuButton GtkWidget *label_widget; GtkWidget *image_widget; GtkWidget *arrow_widget; + GtkWidget *child; GtkArrowType arrow_type; gboolean always_show_arrow; @@ -147,6 +150,7 @@ enum PROP_USE_UNDERLINE, PROP_HAS_FRAME, PROP_PRIMARY, + PROP_CHILD, LAST_PROP }; @@ -158,7 +162,10 @@ enum { static GParamSpec *menu_button_props[LAST_PROP]; static guint signals[LAST_SIGNAL] = { 0 }; -G_DEFINE_TYPE (GtkMenuButton, gtk_menu_button, GTK_TYPE_WIDGET) +static void gtk_menu_button_buildable_iface_init (GtkBuildableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GtkMenuButton, gtk_menu_button, GTK_TYPE_WIDGET, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_menu_button_buildable_iface_init)) static void gtk_menu_button_dispose (GObject *object); @@ -199,6 +206,9 @@ gtk_menu_button_set_property (GObject *object, case PROP_PRIMARY: gtk_menu_button_set_primary (self, g_value_get_boolean (value)); break; + case PROP_CHILD: + gtk_menu_button_set_child (self, g_value_get_object (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -241,6 +251,9 @@ gtk_menu_button_get_property (GObject *object, case PROP_PRIMARY: g_value_set_boolean (value, gtk_menu_button_get_primary (GTK_MENU_BUTTON (object))); break; + case PROP_CHILD: + g_value_set_object (value, gtk_menu_button_get_child (self)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -444,14 +457,14 @@ gtk_menu_button_class_init (GtkMenuButtonClass *klass) /** * GtkMenuButton:always-show-arrow: (attributes org.gtk.Property.get=gtk_menu_button_get_always_show_arrow org.gtk.Property.set=gtk_menu_button_set_always_show_arrow) * - * Whether to show a dropdown arrow even when using an icon. + * Whether to show a dropdown arrow even when using an icon or a custom child. * * Since: 4.4 */ menu_button_props[PROP_ALWAYS_SHOW_ARROW] = g_param_spec_boolean ("always-show-arrow", P_("Always Show Arrow"), - P_("Whether to show a dropdown arrow even when using an icon"), + P_("Whether to show a dropdown arrow even when using an icon or a custom child"), FALSE, GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); @@ -507,6 +520,20 @@ gtk_menu_button_class_init (GtkMenuButtonClass *klass) FALSE, GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + /** + * GtkMenuButton:child: (attributes org.gtk.Property.get=gtk_menu_button_get_child org.gtk.Property.set=gtk_menu_button_set_child) + * + * The child widget. + * + * Since: 4.6 + */ + menu_button_props[PROP_CHILD] = + g_param_spec_object ("child", + P_("Child"), + P_("The child widget"), + GTK_TYPE_WIDGET, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + g_object_class_install_properties (gobject_class, LAST_PROP, menu_button_props); /** @@ -575,13 +602,22 @@ set_arrow_type (GtkWidget *arrow, static void update_style_classes (GtkMenuButton *menu_button) { - if (menu_button->arrow_widget == gtk_button_get_child (GTK_BUTTON (menu_button->button)) || - (menu_button->image_widget && !menu_button->always_show_arrow)) + gboolean has_icon = menu_button->image_widget != NULL; + gboolean has_label = menu_button->label_widget != NULL; + gboolean has_only_arrow = menu_button->arrow_widget == gtk_button_get_child (GTK_BUTTON (menu_button->button)); + gboolean has_arrow = gtk_widget_get_visible (menu_button->arrow_widget); + + if (has_only_arrow || has_icon) gtk_widget_add_css_class (menu_button->button, "image-button"); else gtk_widget_remove_css_class (menu_button->button, "image-button"); - if (menu_button->image_widget && menu_button->always_show_arrow) + if (has_label) + gtk_widget_add_css_class (menu_button->button, "text-button"); + else + gtk_widget_remove_css_class (menu_button->button, "text-button"); + + if (has_arrow && !has_only_arrow) gtk_widget_add_css_class (menu_button->button, "arrow-button"); else gtk_widget_remove_css_class (menu_button->button, "arrow-button"); @@ -635,6 +671,28 @@ gtk_menu_button_init (GtkMenuButton *self) gtk_widget_add_css_class (GTK_WIDGET (self), "popup"); } +static GtkBuildableIface *parent_buildable_iface; + +static void +gtk_menu_button_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const char *type) +{ + if (GTK_IS_WIDGET (child)) + gtk_menu_button_set_child (GTK_MENU_BUTTON (buildable), GTK_WIDGET (child)); + else + parent_buildable_iface->add_child (buildable, builder, child, type); +} + +static void +gtk_menu_button_buildable_iface_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + + iface->add_child = gtk_menu_button_buildable_add_child; +} + /** * gtk_menu_button_new: * @@ -932,6 +990,13 @@ gtk_menu_button_get_popover (GtkMenuButton *menu_button) * @icon_name: the icon name * * Sets the name of an icon to show inside the menu button. + * + * Setting icon name resets [property@Gtk.MenuButton:label] and + * [property@Gtk.MenuButton:child]. + * + * If [property@Gtk.MenuButton:always-show-arrow] is set to `TRUE` and + * [property@Gtk.MenuButton:direction] is not `GTK_ARROW_NONE`, a dropdown arrow + * will be shown next to the icon. */ void gtk_menu_button_set_icon_name (GtkMenuButton *menu_button, @@ -946,6 +1011,9 @@ gtk_menu_button_set_icon_name (GtkMenuButton *menu_button, if (gtk_menu_button_get_label (menu_button)) g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_LABEL]); + if (gtk_menu_button_get_child (menu_button)) + g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_CHILD]); + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_widget_set_halign (box, GTK_ALIGN_CENTER); @@ -963,6 +1031,7 @@ gtk_menu_button_set_icon_name (GtkMenuButton *menu_button, gtk_button_set_child (GTK_BUTTON (menu_button->button), box); menu_button->label_widget = NULL; + menu_button->child = NULL; update_arrow (menu_button); @@ -995,7 +1064,8 @@ gtk_menu_button_get_icon_name (GtkMenuButton *menu_button) * @menu_button: a `GtkMenuButton` * @always_show_arrow: hether to show a dropdown arrow even when using an icon * - * Sets whether to show a dropdown arrow even when using an icon. + * Sets whether to show a dropdown arrow even when using an icon or a custom + * child. * * Since: 4.4 */ @@ -1041,6 +1111,12 @@ gtk_menu_button_get_always_show_arrow (GtkMenuButton *menu_button) * @label: the label * * Sets the label to show inside the menu button. + * + * Setting a label resets [property@Gtk.MenuButton:icon-name] and + * [property@Gtk.MenuButton:child]. + * + * If [property@Gtk.MenuButton:direction] is not `GTK_ARROW_NONE`, a dropdown + * arrow will be shown next to the label. */ void gtk_menu_button_set_label (GtkMenuButton *menu_button, @@ -1056,6 +1132,8 @@ gtk_menu_button_set_label (GtkMenuButton *menu_button, if (gtk_menu_button_get_icon_name (menu_button)) g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_ICON_NAME]); + if (gtk_menu_button_get_child (menu_button)) + g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_CHILD]); box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); label_widget = gtk_label_new (label); @@ -1072,6 +1150,7 @@ gtk_menu_button_set_label (GtkMenuButton *menu_button, menu_button->label_widget = label_widget; menu_button->image_widget = NULL; + menu_button->child = NULL; update_arrow (menu_button); @@ -1354,3 +1433,75 @@ gtk_menu_button_get_primary (GtkMenuButton *menu_button) return menu_button->primary; } + +/** + * gtk_menu_button_set_child: (attributes org.gtk.Method.set_property=child) + * @menu_button: a `GtkMenuButton` + * @child: (nullable): the child widget + * + * Sets the child widget of @menu_button. + * + * Setting a child resets [property@Gtk.MenuButton:label] and + * [property@Gtk.MenuButton:icon-name]. + * + * If [property@Gtk.MenuButton:always-show-arrow] is set to `TRUE` and + * [property@Gtk.MenuButton:direction] is not `GTK_ARROW_NONE`, a dropdown arrow + * will be shown next to the child. + * + * Since: 4.6 + */ +void +gtk_menu_button_set_child (GtkMenuButton *menu_button, + GtkWidget *child) +{ + GtkWidget *box, *arrow; + + g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button)); + g_return_if_fail (child == NULL || GTK_IS_WIDGET (child)); + + g_object_freeze_notify (G_OBJECT (menu_button)); + + if (gtk_menu_button_get_label (menu_button)) + g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_LABEL]); + if (gtk_menu_button_get_icon_name (menu_button)) + g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_ICON_NAME]); + + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_halign (box, GTK_ALIGN_CENTER); + + arrow = gtk_builtin_icon_new ("arrow"); + menu_button->arrow_widget = arrow; + + gtk_box_append (GTK_BOX (box), child); + gtk_box_append (GTK_BOX (box), arrow); + gtk_button_set_child (GTK_BUTTON (menu_button->button), box); + + menu_button->child = child; + + menu_button->image_widget = NULL; + menu_button->label_widget = NULL; + + update_arrow (menu_button); + + g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_CHILD]); + + g_object_thaw_notify (G_OBJECT (menu_button)); +} + +/** + * gtk_menu_button_get_child: (attributes org.gtk.Method.get_property=child) + * @menu_button: a `GtkMenuButton` + * + * Gets the child widget of @menu_button. + * + * Returns: (nullable) (transfer none): the child widget of @menu_button + * + * Since: 4.6 + */ +GtkWidget * +gtk_menu_button_get_child (GtkMenuButton *menu_button) +{ + g_return_val_if_fail (GTK_IS_MENU_BUTTON (menu_button), NULL); + + return menu_button->child; +} diff --git a/gtk/gtkmenubutton.h b/gtk/gtkmenubutton.h index 390771f049..723adb34da 100644 --- a/gtk/gtkmenubutton.h +++ b/gtk/gtkmenubutton.h @@ -121,6 +121,12 @@ void gtk_menu_button_set_primary (GtkMenuButton *menu_button, GDK_AVAILABLE_IN_4_4 gboolean gtk_menu_button_get_primary (GtkMenuButton *menu_button); +GDK_AVAILABLE_IN_4_6 +void gtk_menu_button_set_child (GtkMenuButton *menu_button, + GtkWidget *child); +GDK_AVAILABLE_IN_4_6 +GtkWidget * gtk_menu_button_get_child (GtkMenuButton *menu_button); + G_END_DECLS #endif /* __GTK_MENU_BUTTON_H__ */ diff --git a/gtk/theme/Default/_common.scss b/gtk/theme/Default/_common.scss index dc9a9f6409..a3f3074599 100644 --- a/gtk/theme/Default/_common.scss +++ b/gtk/theme/Default/_common.scss @@ -566,6 +566,15 @@ button { } } + &.arrow-button { + padding-left: 10px; + padding-right: 10px; + + > box { + border-spacing: 4px; + } + } + @at-root %button_basic_drop_active, &:drop(active) { color: $drop_target_color; |