summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Mikhaylenko <alexm@gnome.org>2021-09-03 16:18:06 +0500
committerAlexander Mikhaylenko <alexm@gnome.org>2021-09-03 17:52:46 +0500
commit8b48cf11f9d4fa2916291bfaaed1597b18e6c55e (patch)
tree365113e83f616cd5a7abffaa25a703d9ea406aaa
parente681fdd958751f02a68b472b5bde26086fdeb8cb (diff)
downloadgtk+-8b48cf11f9d4fa2916291bfaaed1597b18e6c55e.tar.gz
menubutton: Support custom children
Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/4205
-rw-r--r--gtk/gtkmenubutton.c169
-rw-r--r--gtk/gtkmenubutton.h6
-rw-r--r--gtk/theme/Default/_common.scss9
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;