diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c index 295fd83119..2638539d7f 100644 --- a/gtk/gtkbutton.c +++ b/gtk/gtkbutton.c @@ -407,6 +407,16 @@ gtk_button_set_child_type (GtkButton *button, guint child_type) if (priv->child_type == child_type) return; + if (child_type == LABEL_CHILD) + gtk_widget_add_css_class (GTK_WIDGET (button), "text-button"); + else + gtk_widget_remove_css_class (GTK_WIDGET (button), "text-button"); + + if (child_type == ICON_CHILD) + gtk_widget_add_css_class (GTK_WIDGET (button), "image-button"); + else + gtk_widget_remove_css_class (GTK_WIDGET (button), "image-button"); + if (child_type != LABEL_CHILD) g_object_notify_by_pspec (G_OBJECT (button), props[PROP_LABEL]); else if (child_type != ICON_CHILD) @@ -835,8 +845,6 @@ gtk_button_set_label (GtkButton *button, gtk_label_set_xalign (GTK_LABEL (child), 0.0); gtk_button_set_child (button, child); - gtk_widget_remove_css_class (GTK_WIDGET (button), "image-button"); - gtk_widget_add_css_class (GTK_WIDGET (button), "text-button"); } gtk_label_set_label (GTK_LABEL (priv->child), label); @@ -966,8 +974,6 @@ gtk_button_set_icon_name (GtkButton *button, NULL); gtk_button_set_child (GTK_BUTTON (button), child); gtk_widget_set_valign (child, GTK_ALIGN_CENTER); - gtk_widget_remove_css_class (GTK_WIDGET (button), "text-button"); - gtk_widget_add_css_class (GTK_WIDGET (button), "image-button"); } else { diff --git a/gtk/gtkmenubutton.c b/gtk/gtkmenubutton.c index 6c9a624831..5e5e70acae 100644 --- a/gtk/gtkmenubutton.c +++ b/gtk/gtkmenubutton.c @@ -66,6 +66,10 @@ * `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 + * `.arrow-button` style class. + * * Inside the toggle button content, there is an `arrow` node for * the indicator, which will carry one of the `.none`, `.up`, `.down`, * `.left` or `.right` style classes to indicate the direction that @@ -85,6 +89,7 @@ #include "gtkactionable.h" #include "gtkbuiltiniconprivate.h" #include "gtkintl.h" +#include "gtkimage.h" #include "gtkmain.h" #include "gtkmenubutton.h" #include "gtkmenubuttonprivate.h" @@ -115,8 +120,10 @@ struct _GtkMenuButton GDestroyNotify create_popup_destroy_notify; GtkWidget *label_widget; + GtkWidget *image_widget; GtkWidget *arrow_widget; GtkArrowType arrow_type; + gboolean always_show_arrow; gboolean primary; }; @@ -133,6 +140,7 @@ enum PROP_DIRECTION, PROP_POPOVER, PROP_ICON_NAME, + PROP_ALWAYS_SHOW_ARROW, PROP_LABEL, PROP_USE_UNDERLINE, PROP_HAS_FRAME, @@ -168,6 +176,9 @@ gtk_menu_button_set_property (GObject *object, case PROP_ICON_NAME: gtk_menu_button_set_icon_name (self, g_value_get_string (value)); break; + case PROP_ALWAYS_SHOW_ARROW: + gtk_menu_button_set_always_show_arrow (self, g_value_get_boolean (value)); + break; case PROP_LABEL: gtk_menu_button_set_label (self, g_value_get_string (value)); break; @@ -207,6 +218,9 @@ gtk_menu_button_get_property (GObject *object, case PROP_ICON_NAME: g_value_set_string (value, gtk_menu_button_get_icon_name (GTK_MENU_BUTTON (object))); break; + case PROP_ALWAYS_SHOW_ARROW: + g_value_set_boolean (value, gtk_menu_button_get_always_show_arrow (self)); + break; case PROP_LABEL: g_value_set_string (value, gtk_menu_button_get_label (GTK_MENU_BUTTON (object))); break; @@ -411,6 +425,20 @@ gtk_menu_button_class_init (GtkMenuButtonClass *klass) NULL, GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + /** + * 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. + * + * 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"), + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + /** * GtkMenuButton:label: (attributes org.gtk.Property.get=gtk_menu_button_get_label org.gtk.Property.set=gtk_menu_button_set_label) * @@ -504,12 +532,48 @@ set_arrow_type (GtkWidget *arrow, gtk_widget_hide (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)) + 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) + gtk_widget_add_css_class (menu_button->button, "arrow-button"); + else + gtk_widget_remove_css_class (menu_button->button, "arrow-button"); +} + +static void +update_arrow (GtkMenuButton *menu_button) +{ + gboolean has_only_arrow, is_text_button; + + if (menu_button->arrow_widget == NULL) + return; + + has_only_arrow = menu_button->arrow_widget == gtk_button_get_child (GTK_BUTTON (menu_button->button)); + is_text_button = menu_button->label_widget != NULL; + + set_arrow_type (menu_button->arrow_widget, + menu_button->arrow_type, + has_only_arrow || + ((is_text_button || menu_button->always_show_arrow) && + (menu_button->arrow_type != GTK_ARROW_NONE))); + + update_style_classes (menu_button); +} + static void add_arrow (GtkMenuButton *self) { GtkWidget *arrow; arrow = gtk_builtin_icon_new ("arrow"); + gtk_widget_set_halign (arrow, GTK_ALIGN_CENTER); set_arrow_type (arrow, self->arrow_type, TRUE); gtk_button_set_child (GTK_BUTTON (self->button), arrow); self->arrow_widget = arrow; @@ -524,6 +588,7 @@ gtk_menu_button_init (GtkMenuButton *self) gtk_widget_set_parent (self->button, GTK_WIDGET (self)); g_signal_connect_swapped (self->button, "toggled", G_CALLBACK (gtk_menu_button_toggled), self); add_arrow (self); + update_style_classes (self); gtk_widget_set_sensitive (self->button, FALSE); @@ -691,8 +756,6 @@ void gtk_menu_button_set_direction (GtkMenuButton *menu_button, GtkArrowType direction) { - gboolean is_image_button; - g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button)); if (menu_button->arrow_type == direction) @@ -701,14 +764,7 @@ gtk_menu_button_set_direction (GtkMenuButton *menu_button, menu_button->arrow_type = direction; g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_DIRECTION]); - /* Is it custom content? We don't change that */ - is_image_button = menu_button->label_widget == NULL; - if (is_image_button && (menu_button->arrow_widget != gtk_button_get_child (GTK_BUTTON (menu_button->button)))) - return; - - set_arrow_type (menu_button->arrow_widget, - menu_button->arrow_type, - is_image_button || (menu_button->arrow_type != GTK_ARROW_NONE)); + update_arrow (menu_button); update_popover_direction (menu_button); } @@ -841,10 +897,38 @@ void gtk_menu_button_set_icon_name (GtkMenuButton *menu_button, const char *icon_name) { + GtkWidget *box, *image_widget, *arrow; + g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button)); - gtk_button_set_icon_name (GTK_BUTTON (menu_button->button), icon_name); + 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]); + + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_halign (box, GTK_ALIGN_CENTER); + + image_widget = g_object_new (GTK_TYPE_IMAGE, + "accessible-role", GTK_ACCESSIBLE_ROLE_PRESENTATION, + "icon-name", icon_name, + NULL); + menu_button->image_widget = image_widget; + + arrow = gtk_builtin_icon_new ("arrow"); + menu_button->arrow_widget = arrow; + + gtk_box_append (GTK_BOX (box), image_widget); + gtk_box_append (GTK_BOX (box), arrow); + gtk_button_set_child (GTK_BUTTON (menu_button->button), box); + + menu_button->label_widget = NULL; + + update_arrow (menu_button); + g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_ICON_NAME]); + + g_object_thaw_notify (G_OBJECT (menu_button)); } /** @@ -860,7 +944,55 @@ gtk_menu_button_get_icon_name (GtkMenuButton *menu_button) { g_return_val_if_fail (GTK_IS_MENU_BUTTON (menu_button), NULL); - return gtk_button_get_icon_name (GTK_BUTTON (menu_button->button)); + if (menu_button->image_widget) + return gtk_image_get_icon_name (GTK_IMAGE (menu_button->image_widget)); + + return NULL; +} + +/** + * gtk_menu_button_set_always_show_arrow: (attributes org.gtk.Method.set_property=always-show-arrow) + * @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. + * + * Since: 4.4 + */ +void +gtk_menu_button_set_always_show_arrow (GtkMenuButton *menu_button, + gboolean always_show_arrow) +{ + g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button)); + + always_show_arrow = !!always_show_arrow; + + if (always_show_arrow == menu_button->always_show_arrow) + return; + + menu_button->always_show_arrow = always_show_arrow; + + update_arrow (menu_button); + + g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_ALWAYS_SHOW_ARROW]); +} + +/** + * gtk_menu_button_get_always_show_arrow: (attributes org.gtk.Method.get_property=always-show-arrow) + * @menu_button: a `GtkMenuButton` + * + * Gets whether to show a dropdown arrow even when using an icon. + * + * Returns: whether to show a dropdown arrow even when using an icon + * + * Since: 4.4 + */ +gboolean +gtk_menu_button_get_always_show_arrow (GtkMenuButton *menu_button) +{ + g_return_val_if_fail (GTK_IS_MENU_BUTTON (menu_button), FALSE); + + return menu_button->always_show_arrow; } /** @@ -880,9 +1012,13 @@ gtk_menu_button_set_label (GtkMenuButton *menu_button, g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button)); - box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + g_object_freeze_notify (G_OBJECT (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]); + + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); label_widget = gtk_label_new (label); - g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button)); gtk_label_set_xalign (GTK_LABEL (label_widget), 0); gtk_label_set_use_underline (GTK_LABEL (label_widget), gtk_button_get_use_underline (GTK_BUTTON (menu_button->button))); @@ -890,13 +1026,18 @@ gtk_menu_button_set_label (GtkMenuButton *menu_button, gtk_widget_set_halign (label_widget, GTK_ALIGN_CENTER); arrow = gtk_builtin_icon_new ("arrow"); menu_button->arrow_widget = arrow; - set_arrow_type (arrow, menu_button->arrow_type, menu_button->arrow_type != GTK_ARROW_NONE); gtk_box_append (GTK_BOX (box), label_widget); gtk_box_append (GTK_BOX (box), arrow); gtk_button_set_child (GTK_BUTTON (menu_button->button), box); menu_button->label_widget = label_widget; + menu_button->image_widget = NULL; + + update_arrow (menu_button); + g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_LABEL]); + + g_object_thaw_notify (G_OBJECT (menu_button)); } /** @@ -910,16 +1051,10 @@ gtk_menu_button_set_label (GtkMenuButton *menu_button, const char * gtk_menu_button_get_label (GtkMenuButton *menu_button) { - GtkWidget *child; - g_return_val_if_fail (GTK_IS_MENU_BUTTON (menu_button), NULL); - child = gtk_button_get_child (GTK_BUTTON (menu_button->button)); - if (GTK_IS_BOX (child)) - { - child = gtk_widget_get_first_child (child); - return gtk_label_get_label (GTK_LABEL (child)); - } + if (menu_button->label_widget) + return gtk_label_get_label (GTK_LABEL (menu_button->label_widget)); return NULL; } diff --git a/gtk/gtkmenubutton.h b/gtk/gtkmenubutton.h index af71beedd6..390771f049 100644 --- a/gtk/gtkmenubutton.h +++ b/gtk/gtkmenubutton.h @@ -80,6 +80,12 @@ void gtk_menu_button_set_icon_name (GtkMenuButton *menu_button, GDK_AVAILABLE_IN_ALL const char * gtk_menu_button_get_icon_name (GtkMenuButton *menu_button); +GDK_AVAILABLE_IN_4_4 +void gtk_menu_button_set_always_show_arrow (GtkMenuButton *menu_button, + gboolean always_show_arrow); +GDK_AVAILABLE_IN_4_4 +gboolean gtk_menu_button_get_always_show_arrow (GtkMenuButton *menu_button); + GDK_AVAILABLE_IN_ALL void gtk_menu_button_set_label (GtkMenuButton *menu_button, const char *label); diff --git a/gtk/theme/Default/_common.scss b/gtk/theme/Default/_common.scss index a4db8c7301..dc9a9f6409 100644 --- a/gtk/theme/Default/_common.scss +++ b/gtk/theme/Default/_common.scss @@ -4322,6 +4322,10 @@ statusbar { } menubutton { + > button > box { + border-spacing: 6px; + } + arrow { min-height: 16px; min-width: 16px;