menubutton: Don't derive from GtkToggleButton

Make GtkMenuButton a widget that has a
toggle button, instead of deriving from it.

We give it icon-name and label properties,
to let people do what they expect to do
with menu buttons.
This commit is contained in:
Matthias Clasen 2019-05-18 04:56:30 +00:00
parent 7cec5323d1
commit 6a7d70cc9f
3 changed files with 186 additions and 43 deletions

View File

@ -1850,6 +1850,10 @@ gtk_menu_button_set_direction
gtk_menu_button_get_direction
gtk_menu_button_set_align_widget
gtk_menu_button_get_align_widget
gtk_menu_button_set_icon_name
gtk_menu_button_get_icon_name
gtk_menu_button_set_label
gtk_menu_button_get_label
<SUBSECTION Standard>
GTK_TYPE_MENU_BUTTON
GTK_MENU_BUTTON

View File

@ -27,9 +27,9 @@
* This popup can be provided either as a #GtkMenu, a #GtkPopover or an
* abstract #GMenuModel.
*
* The #GtkMenuButton widget can hold any valid child widget. That is, it
* can hold almost any other standard #GtkWidget. The most commonly used
* child is #GtkImage. If no widget is explicitely added to the #GtkMenuButton,
* The #GtkMenuButton widget can show either an icon (set with the
* #GtkMenuButton:icon-name property) or a label (set with the
* #GtkMenuButton:label property). If neither is explicitly set,
* a #GtkImage is automatically created, using an arrow image oriented
* according to #GtkMenuButton:direction or the generic open-menu-symbolic
* icon if the direction is not set.
@ -121,11 +121,14 @@
#include "gtkprivate.h"
#include "gtkstylecontext.h"
#include "gtktypebuiltins.h"
#include "gtklabel.h"
#include "gtkbox.h"
#include "a11y/gtkmenubuttonaccessible.h"
struct _GtkMenuButtonPrivate
{
GtkWidget *button;
GtkWidget *menu; /* The menu and the popover are mutually exclusive */
GtkWidget *popover; /* Only one at a time can be set */
GMenuModel *model;
@ -136,7 +139,8 @@ struct _GtkMenuButtonPrivate
GtkWidget *align_widget;
GtkWidget *arrow_widget;
GtkArrowType arrow_type;
gboolean use_popover;
guint use_popover : 1;
guint press_handled : 1;
};
@ -149,12 +153,14 @@ enum
PROP_DIRECTION,
PROP_USE_POPOVER,
PROP_POPOVER,
PROP_ICON_NAME,
PROP_LABEL,
LAST_PROP
};
static GParamSpec *menu_button_props[LAST_PROP];
G_DEFINE_TYPE_WITH_PRIVATE (GtkMenuButton, gtk_menu_button, GTK_TYPE_TOGGLE_BUTTON)
G_DEFINE_TYPE_WITH_PRIVATE (GtkMenuButton, gtk_menu_button, GTK_TYPE_WIDGET)
static void gtk_menu_button_dispose (GObject *object);
@ -186,6 +192,12 @@ gtk_menu_button_set_property (GObject *object,
case PROP_POPOVER:
gtk_menu_button_set_popover (self, g_value_get_object (value));
break;
case PROP_ICON_NAME:
gtk_menu_button_set_icon_name (self, g_value_get_string (value));
break;
case PROP_LABEL:
gtk_menu_button_set_label (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@ -219,6 +231,12 @@ gtk_menu_button_get_property (GObject *object,
case PROP_POPOVER:
g_value_set_object (value, priv->popover);
break;
case PROP_ICON_NAME:
g_value_set_string (value, gtk_menu_button_get_icon_name (GTK_MENU_BUTTON (object)));
break;
case PROP_LABEL:
g_value_set_string (value, gtk_menu_button_get_label (GTK_MENU_BUTTON (object)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@ -402,11 +420,10 @@ popup_menu (GtkMenuButton *menu_button,
}
static void
gtk_menu_button_toggled (GtkToggleButton *button)
gtk_menu_button_toggled (GtkMenuButton *menu_button)
{
GtkMenuButton *menu_button = GTK_MENU_BUTTON (button);
GtkMenuButtonPrivate *priv = gtk_menu_button_get_instance_private (menu_button);
gboolean active = gtk_toggle_button_get_active (button);
gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button));
if (priv->menu)
{
@ -434,35 +451,39 @@ gtk_menu_button_toggled (GtkToggleButton *button)
else
gtk_popover_popdown (GTK_POPOVER (priv->popover));
}
if (GTK_TOGGLE_BUTTON_CLASS (gtk_menu_button_parent_class)->toggled)
GTK_TOGGLE_BUTTON_CLASS (gtk_menu_button_parent_class)->toggled (button);
}
static void
gtk_menu_button_add (GtkContainer *container,
GtkWidget *child)
gtk_menu_button_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkMenuButton *button = GTK_MENU_BUTTON (container);
GtkMenuButtonPrivate *priv = gtk_menu_button_get_instance_private (button);
GtkMenuButton *menu_button = GTK_MENU_BUTTON (widget);
GtkMenuButtonPrivate *priv = gtk_menu_button_get_instance_private (menu_button);
if (priv->arrow_widget)
gtk_container_remove (container, priv->arrow_widget);
GTK_CONTAINER_CLASS (gtk_menu_button_parent_class)->add (container, child);
gtk_widget_measure (priv->button,
orientation,
for_size,
minimum, natural,
minimum_baseline, natural_baseline);
}
static void
gtk_menu_button_remove (GtkContainer *container,
GtkWidget *child)
gtk_menu_button_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkMenuButton *button = GTK_MENU_BUTTON (container);
GtkMenuButtonPrivate *priv = gtk_menu_button_get_instance_private (button);
GtkMenuButton *menu_button = GTK_MENU_BUTTON (widget);
GtkMenuButtonPrivate *priv = gtk_menu_button_get_instance_private (menu_button);
if (child == priv->arrow_widget)
priv->arrow_widget = NULL;
GTK_CONTAINER_CLASS (gtk_menu_button_parent_class)->remove (container, child);
gtk_widget_size_allocate (priv->button,
&(GtkAllocation) { 0, 0, width, height },
baseline);
}
static void
@ -470,19 +491,14 @@ gtk_menu_button_class_init (GtkMenuButtonClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
GtkToggleButtonClass *toggle_button_class = GTK_TOGGLE_BUTTON_CLASS (klass);
gobject_class->set_property = gtk_menu_button_set_property;
gobject_class->get_property = gtk_menu_button_get_property;
gobject_class->dispose = gtk_menu_button_dispose;
widget_class->state_flags_changed = gtk_menu_button_state_flags_changed;
container_class->add = gtk_menu_button_add;
container_class->remove = gtk_menu_button_remove;
toggle_button_class->toggled = gtk_menu_button_toggled;
widget_class->measure = gtk_menu_button_measure;
widget_class->size_allocate = gtk_menu_button_size_allocate;
/**
* GtkMenuButton:popup:
@ -564,10 +580,24 @@ gtk_menu_button_class_init (GtkMenuButtonClass *klass)
GTK_TYPE_POPOVER,
G_PARAM_READWRITE);
menu_button_props[PROP_ICON_NAME] =
g_param_spec_string ("icon-name",
P_("Icon Name"),
P_("The name of the icon used to automatically populate the button"),
NULL,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
menu_button_props[PROP_LABEL] =
g_param_spec_string ("label",
P_("Label"),
P_("The label for the button"),
NULL,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, LAST_PROP, menu_button_props);
gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_MENU_BUTTON_ACCESSIBLE);
gtk_widget_class_set_css_name (widget_class, I_("button"));
gtk_widget_class_set_css_name (widget_class, I_("menubutton"));
}
static void
@ -604,7 +634,7 @@ add_arrow (GtkMenuButton *menu_button)
arrow = gtk_image_new ();
set_arrow_type (GTK_IMAGE (arrow), priv->arrow_type);
gtk_container_add (GTK_CONTAINER (menu_button), arrow);
gtk_container_add (GTK_CONTAINER (priv->button), arrow);
priv->arrow_widget = arrow;
}
@ -614,9 +644,14 @@ gtk_menu_button_init (GtkMenuButton *menu_button)
GtkMenuButtonPrivate *priv = gtk_menu_button_get_instance_private (menu_button);
GtkStyleContext *context;
gtk_widget_set_has_surface (GTK_WIDGET (menu_button), FALSE);
priv->arrow_type = GTK_ARROW_DOWN;
priv->use_popover = TRUE;
priv->button = gtk_toggle_button_new ();
gtk_widget_set_parent (priv->button, GTK_WIDGET (menu_button));
g_signal_connect_swapped (priv->button, "toggled", G_CALLBACK (gtk_menu_button_toggled), menu_button);
add_arrow (menu_button);
gtk_widget_set_sensitive (GTK_WIDGET (menu_button), FALSE);
@ -648,7 +683,9 @@ gtk_menu_button_new (void)
static gboolean
menu_deactivate_cb (GtkMenuButton *menu_button)
{
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menu_button), FALSE);
GtkMenuButtonPrivate *priv = gtk_menu_button_get_instance_private (menu_button);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), FALSE);
return TRUE;
}
@ -669,9 +706,6 @@ update_sensitivity (GtkMenuButton *menu_button)
{
GtkMenuButtonPrivate *priv = gtk_menu_button_get_instance_private (menu_button);
if (gtk_actionable_get_action_name (GTK_ACTIONABLE (menu_button)) != NULL)
return;
gtk_widget_set_sensitive (GTK_WIDGET (menu_button),
priv->menu != NULL || priv->popover != NULL);
}
@ -990,7 +1024,7 @@ gtk_menu_button_set_direction (GtkMenuButton *menu_button,
g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_DIRECTION]);
/* Is it custom content? We don't change that */
child = gtk_bin_get_child (GTK_BIN (menu_button));
child = gtk_bin_get_child (GTK_BIN (priv->button));
if (priv->arrow_widget != child)
return;
@ -1045,6 +1079,7 @@ gtk_menu_button_dispose (GObject *object)
set_align_widget_pointer (GTK_MENU_BUTTON (object), NULL);
g_clear_object (&priv->model);
g_clear_pointer (&priv->button, gtk_widget_unparent);
G_OBJECT_CLASS (gtk_menu_button_parent_class)->dispose (object);
}
@ -1182,3 +1217,95 @@ gtk_menu_button_get_popover (GtkMenuButton *menu_button)
return GTK_POPOVER (priv->popover);
}
/**
* gtk_menu_button_set_icon_name:
* @menu_button: a #GtkMenuButton
* @icon_name: the icon name
*
* Sets the name of an icon to show inside the menu button.
*/
void
gtk_menu_button_set_icon_name (GtkMenuButton *menu_button,
const char *icon_name)
{
GtkMenuButtonPrivate *priv = gtk_menu_button_get_instance_private (menu_button);
g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button));
gtk_button_set_icon_name (GTK_BUTTON (priv->button), icon_name);
g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_ICON_NAME]);
}
/**
* gtk_menu_button_get_icon_name:
* @menu_button: a #GtkMenuButton
*
* Gets the name of the icon shown in the button.
*
* Returns: the name of the icon shown in the button
*/
const char *
gtk_menu_button_get_icon_name (GtkMenuButton *menu_button)
{
GtkMenuButtonPrivate *priv = gtk_menu_button_get_instance_private (menu_button);
g_return_val_if_fail (GTK_IS_MENU_BUTTON (menu_button), NULL);
return gtk_button_get_icon_name (GTK_BUTTON (priv->button));
}
/**
* gtk_menu_button_set_label:
* @menu_button: a #GtkMenuButton
* @icon_name: the label
*
* Sets the label to show inside the menu button.
*/
void
gtk_menu_button_set_label (GtkMenuButton *menu_button,
const char *label)
{
GtkMenuButtonPrivate *priv = gtk_menu_button_get_instance_private (menu_button);
GtkWidget *child;
GtkWidget *box;
g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button));
child = gtk_bin_get_child (GTK_BIN (priv->button));
if (child)
gtk_container_remove (GTK_CONTAINER (priv->button), child);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_container_add (GTK_CONTAINER (box), gtk_label_new (label));
gtk_container_add (GTK_CONTAINER (box), gtk_image_new_from_icon_name ("pan-down-symbolic"));
gtk_container_add (GTK_CONTAINER (priv->button), box);
g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_LABEL]);
}
/**
* gtk_menu_button_get_label:
* @menu_button: a #GtkMenuButton
*
* Gets the label shown in the button
*
* Returns: the label shown in the button
*/
const char *
gtk_menu_button_get_label (GtkMenuButton *menu_button)
{
GtkMenuButtonPrivate *priv = gtk_menu_button_get_instance_private (menu_button);
GtkWidget *child;
g_return_val_if_fail (GTK_IS_MENU_BUTTON (menu_button), NULL);
child = gtk_bin_get_child (GTK_BIN (priv->button));
if (GTK_IS_BOX (child))
{
child = gtk_widget_get_first_child (child);
return gtk_label_get_label (GTK_LABEL (child));
}
return NULL;
}

View File

@ -44,12 +44,12 @@ typedef struct _GtkMenuButtonPrivate GtkMenuButtonPrivate;
struct _GtkMenuButton
{
GtkToggleButton parent_instance;
GtkWidget parent_instance;
};
struct _GtkMenuButtonClass
{
GtkToggleButtonClass parent_class;
GtkWidgetClass parent_class;
/* Padding for future expansion */
void (*_gtk_reserved1) (void);
@ -100,6 +100,18 @@ void gtk_menu_button_set_use_popover (GtkMenuButton *menu_button,
GDK_AVAILABLE_IN_ALL
gboolean gtk_menu_button_get_use_popover (GtkMenuButton *menu_button);
GDK_AVAILABLE_IN_ALL
void gtk_menu_button_set_icon_name (GtkMenuButton *menu_button,
const char *icon_name);
GDK_AVAILABLE_IN_ALL
const char * gtk_menu_button_get_icon_name (GtkMenuButton *menu_button);
GDK_AVAILABLE_IN_ALL
void gtk_menu_button_set_label (GtkMenuButton *menu_button,
const char *label);
GDK_AVAILABLE_IN_ALL
const char * gtk_menu_button_get_label (GtkMenuButton *menu_button);
G_END_DECLS