diff options
author | Matthias Clasen <mclasen@redhat.com> | 2015-11-02 23:42:14 -0500 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2015-11-02 23:42:14 -0500 |
commit | aede5c65d336e288f3054f7ada888031f68b366d (patch) | |
tree | fba14885423ed156731f1624df2738f2f90efed1 | |
parent | ac553d7e44f44e161c204a04ab8480a9b619304c (diff) | |
download | gtk+-aede5c65d336e288f3054f7ada888031f68b366d.tar.gz |
menu item: Use CSS nodes
Use the element name menuitem for GtkMenuItem, GtkCheckMenuItem
and GtkRadioMenuItem. GtkSeparatorMenuItem gets the name separator.
Add a subnode with name arrow if a submenu is attached.
Give the radio and check menu items a subnode with name check or
radio.
-rw-r--r-- | gtk/gtkcheckmenuitem.c | 150 | ||||
-rw-r--r-- | gtk/gtkmenuitem.c | 125 | ||||
-rw-r--r-- | gtk/gtkmenuitemprivate.h | 3 | ||||
-rw-r--r-- | gtk/gtkradiomenuitem.c | 5 | ||||
-rw-r--r-- | gtk/gtkseparatormenuitem.c | 7 |
5 files changed, 247 insertions, 43 deletions
diff --git a/gtk/gtkcheckmenuitem.c b/gtk/gtkcheckmenuitem.c index 9e61306c8c..6a1ad2298e 100644 --- a/gtk/gtkcheckmenuitem.c +++ b/gtk/gtkcheckmenuitem.c @@ -32,6 +32,9 @@ #include "gtkprivate.h" #include "gtkintl.h" #include "a11y/gtkcheckmenuitemaccessible.h" +#include "gtkcssnodeprivate.h" +#include "gtkcssstylepropertyprivate.h" +#include "gtkwidgetprivate.h" /** * SECTION:gtkcheckmenuitem @@ -45,6 +48,11 @@ * A check box indicating the state of the boolean value is displayed * at the left side of the #GtkMenuItem. Activating the #GtkMenuItem * toggles the value. + * + * # CSS nodes + * + * GtkCheckMenuItem has a main CSS node with name menuitem, and a subnode + * with name check, which gets the .left or .right style class. */ @@ -52,6 +60,8 @@ struct _GtkCheckMenuItemPrivate { + GtkCssNode *indicator_node; + guint active : 1; guint draw_as_radio : 1; guint inconsistent : 1; @@ -85,6 +95,11 @@ static void gtk_check_menu_item_get_property (GObject *obj GValue *value, GParamSpec *pspec); +static void gtk_check_menu_item_state_flags_changed (GtkWidget *widget, + GtkStateFlags previous_state); +static void gtk_check_menu_item_direction_changed (GtkWidget *widget, + GtkTextDirection previous_dir); + static void gtk_check_menu_item_activatable_interface_init (GtkActivatableIface *iface); static void gtk_check_menu_item_update (GtkActivatable *activatable, GtkAction *action, @@ -116,6 +131,9 @@ gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass) gobject_class->set_property = gtk_check_menu_item_set_property; gobject_class->get_property = gtk_check_menu_item_get_property; + widget_class->state_flags_changed = gtk_check_menu_item_state_flags_changed; + widget_class->direction_changed = gtk_check_menu_item_direction_changed; + g_object_class_install_property (gobject_class, PROP_ACTIVE, g_param_spec_boolean ("active", @@ -151,8 +169,6 @@ gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass) widget_class->draw = gtk_check_menu_item_draw; - gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_CHECK_MENU_ITEM_ACCESSIBLE); - menu_item_class->activate = gtk_check_menu_item_activate; menu_item_class->hide_on_activate = FALSE; menu_item_class->toggle_size_request = gtk_check_menu_item_toggle_size_request; @@ -177,6 +193,9 @@ gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass) NULL, NULL, _gtk_marshal_VOID__VOID, G_TYPE_NONE, 0); + + gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_CHECK_MENU_ITEM_ACCESSIBLE); + gtk_widget_class_set_css_name (widget_class, "menuitem"); } static void @@ -378,6 +397,22 @@ gtk_check_menu_item_toggled (GtkCheckMenuItem *check_menu_item) g_signal_emit (check_menu_item, check_menu_item_signals[TOGGLED], 0); } +static void +update_node_state (GtkCheckMenuItem *check_menu_item) +{ + GtkCheckMenuItemPrivate *priv = check_menu_item->priv; + GtkStateFlags state; + + state = gtk_widget_get_state_flags (GTK_WIDGET (check_menu_item)); + + if (priv->inconsistent) + state |= GTK_STATE_FLAG_INCONSISTENT; + if (priv->active) + state |= GTK_STATE_FLAG_CHECKED; + + gtk_css_node_set_state (priv->indicator_node, state); +} + /** * gtk_check_menu_item_set_inconsistent: * @check_menu_item: a #GtkCheckMenuItem @@ -408,6 +443,7 @@ gtk_check_menu_item_set_inconsistent (GtkCheckMenuItem *check_menu_item, if (setting != priv->inconsistent) { priv->inconsistent = setting; + update_node_state (check_menu_item); gtk_widget_queue_draw (GTK_WIDGET (check_menu_item)); g_object_notify (G_OBJECT (check_menu_item), "inconsistent"); } @@ -453,6 +489,10 @@ gtk_check_menu_item_set_draw_as_radio (GtkCheckMenuItem *check_menu_item, if (draw_as_radio != priv->draw_as_radio) { priv->draw_as_radio = draw_as_radio; + if (draw_as_radio) + gtk_css_node_set_name (priv->indicator_node, I_("radio")); + else + gtk_css_node_set_name (priv->indicator_node, I_("checl")); gtk_widget_queue_draw (GTK_WIDGET (check_menu_item)); @@ -479,10 +519,44 @@ gtk_check_menu_item_get_draw_as_radio (GtkCheckMenuItem *check_menu_item) } static void +node_style_changed_cb (GtkCssNode *node, + GtkCssStyle *old_style, + GtkCssStyle *new_style, + GtkWidget *widget) +{ + GtkBitmask *changes; + static GtkBitmask *affects_size = NULL; + + if (G_UNLIKELY (affects_size == NULL)) + affects_size = _gtk_css_style_property_get_mask_affecting (GTK_CSS_AFFECTS_SIZE | GTK_CSS_AFFECTS_CLIP); + + changes = _gtk_bitmask_new (); + changes = gtk_css_style_add_difference (changes, old_style, new_style); + + if (_gtk_bitmask_intersects (changes, affects_size)) + gtk_widget_queue_resize (widget); + else + gtk_widget_queue_draw (widget); + + _gtk_bitmask_free (changes); +} + +static void gtk_check_menu_item_init (GtkCheckMenuItem *check_menu_item) { - check_menu_item->priv = gtk_check_menu_item_get_instance_private (check_menu_item); - check_menu_item->priv->active = FALSE; + GtkCheckMenuItemPrivate *priv; + GtkCssNode *widget_node; + + priv = check_menu_item->priv = gtk_check_menu_item_get_instance_private (check_menu_item); + priv->active = FALSE; + + widget_node = gtk_widget_get_css_node (GTK_WIDGET (check_menu_item)); + priv->indicator_node = gtk_css_node_new (); + gtk_css_node_set_name (priv->indicator_node, I_("check")); + gtk_css_node_set_parent (priv->indicator_node, widget_node); + gtk_css_node_set_state (priv->indicator_node, gtk_css_node_get_state (widget_node)); + g_signal_connect_object (priv->indicator_node, "style-changed", G_CALLBACK (node_style_changed_cb), check_menu_item, 0); + g_object_unref (priv->indicator_node); } static gint @@ -511,6 +585,7 @@ gtk_check_menu_item_activate (GtkMenuItem *menu_item) priv->active = !priv->active; gtk_check_menu_item_toggled (check_menu_item); + update_node_state (check_menu_item); gtk_widget_queue_draw (GTK_WIDGET (check_menu_item)); GTK_MENU_ITEM_CLASS (gtk_check_menu_item_parent_class)->activate (menu_item); @@ -519,6 +594,50 @@ gtk_check_menu_item_activate (GtkMenuItem *menu_item) } static void +gtk_check_menu_item_state_flags_changed (GtkWidget *widget, + GtkStateFlags previous_state) + +{ + GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (widget); + + update_node_state (check_menu_item); + + GTK_WIDGET_CLASS (gtk_check_menu_item_parent_class)->state_flags_changed (widget, previous_state); +} + +static void +gtk_check_menu_item_direction_changed (GtkWidget *widget, + GtkTextDirection previous_dir) +{ + GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (widget); + GtkCheckMenuItemPrivate *priv = check_menu_item->priv; + GtkCssNode *widget_node, *node; + + widget_node = gtk_widget_get_css_node (widget); + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + { + gtk_css_node_remove_class (priv->indicator_node, g_quark_from_static_string (GTK_STYLE_CLASS_LEFT)); + gtk_css_node_add_class (priv->indicator_node, g_quark_from_static_string (GTK_STYLE_CLASS_RIGHT)); + + node = gtk_css_node_get_last_child (widget_node); + if (node != priv->indicator_node) + gtk_css_node_insert_after (widget_node, priv->indicator_node, node); + } + else + { + gtk_css_node_add_class (priv->indicator_node, g_quark_from_static_string (GTK_STYLE_CLASS_LEFT)); + gtk_css_node_remove_class (priv->indicator_node, g_quark_from_static_string (GTK_STYLE_CLASS_RIGHT)); + + node = gtk_css_node_get_first_child (widget_node); + if (node != priv->indicator_node) + gtk_css_node_insert_before (widget_node, priv->indicator_node, node); + } + + GTK_WIDGET_CLASS (gtk_check_menu_item_parent_class)->direction_changed (widget, previous_dir); +} + +static void gtk_real_check_menu_item_draw_indicator (GtkCheckMenuItem *check_menu_item, cairo_t *cr) { @@ -571,27 +690,14 @@ gtk_real_check_menu_item_draw_indicator (GtkCheckMenuItem *check_menu_item, y = (allocation.height - indicator_size) / 2; - gtk_style_context_save (context); - - if (priv->inconsistent) - state |= GTK_STATE_FLAG_INCONSISTENT; - if (priv->active) - state |= GTK_STATE_FLAG_CHECKED; - - gtk_style_context_set_state (context, state); + gtk_style_context_save_to_node (context, priv->indicator_node); if (priv->draw_as_radio) - { - gtk_style_context_add_class (context, GTK_STYLE_CLASS_RADIO); - gtk_render_option (context, cr, x, y, - indicator_size, indicator_size); - } + gtk_render_option (context, cr, x, y, + indicator_size, indicator_size); else - { - gtk_style_context_add_class (context, GTK_STYLE_CLASS_CHECK); - gtk_render_check (context, cr, x, y, - indicator_size, indicator_size); - } + gtk_render_check (context, cr, x, y, + indicator_size, indicator_size); gtk_style_context_restore (context); } diff --git a/gtk/gtkmenuitem.c b/gtk/gtkmenuitem.c index 600b0eb907..450fb70e5a 100644 --- a/gtk/gtkmenuitem.c +++ b/gtk/gtkmenuitem.c @@ -44,6 +44,8 @@ #include "gtktypebuiltins.h" #include "a11y/gtkmenuitemaccessible.h" #include "deprecated/gtktearoffmenuitem.h" +#include "gtkstylecontextprivate.h" +#include "gtkcssstylepropertyprivate.h" #define MENU_POPUP_DELAY 225 @@ -85,6 +87,12 @@ * </child> * </object> * ]| + * + * # CSS nodes + * + * GtkMenuItem has a single CSS node with name menuitem. If the menuitem + * has a submenu, it gets another CSS node with name arrow, which has + * the .left or .right style class. */ @@ -141,6 +149,8 @@ static gboolean gtk_menu_item_draw (GtkWidget *widget, cairo_t *cr); static void gtk_menu_item_parent_set (GtkWidget *widget, GtkWidget *previous_parent); +static void gtk_menu_item_direction_changed (GtkWidget *widget, + GtkTextDirection previous_dir); static void gtk_real_menu_item_select (GtkMenuItem *item); @@ -162,10 +172,12 @@ static void gtk_menu_item_position_menu (GtkMenu *menu, gboolean *push_in, gpointer user_data); static void gtk_menu_item_show_all (GtkWidget *widget); + static void gtk_menu_item_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); + static gboolean gtk_menu_item_can_activate_accel (GtkWidget *widget, guint signal_id); @@ -299,8 +311,7 @@ gtk_menu_item_class_init (GtkMenuItemClass *klass) widget_class->get_preferred_width = gtk_menu_item_get_preferred_width; widget_class->get_preferred_height = gtk_menu_item_get_preferred_height; widget_class->get_preferred_height_for_width = gtk_menu_item_get_preferred_height_for_width; - - gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_MENU_ITEM_ACCESSIBLE); + widget_class->direction_changed = gtk_menu_item_direction_changed; container_class->forall = gtk_menu_item_forall; @@ -534,12 +545,14 @@ gtk_menu_item_class_init (GtkMenuItemClass *klass) P_("The minimum desired width of the menu item in characters"), 0, G_MAXINT, 12, GTK_PARAM_READABLE)); + + gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_MENU_ITEM_ACCESSIBLE); + gtk_widget_class_set_css_name (widget_class, "menuitem"); } static void gtk_menu_item_init (GtkMenuItem *menu_item) { - GtkStyleContext *context; GtkMenuItemPrivate *priv; priv = gtk_menu_item_get_instance_private (menu_item); @@ -562,9 +575,6 @@ gtk_menu_item_init (GtkMenuItem *menu_item) priv->use_action_appearance = TRUE; priv->timer = 0; priv->action = NULL; - - context = gtk_widget_get_style_context (GTK_WIDGET (menu_item)); - gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENUITEM); } /** @@ -762,6 +772,12 @@ gtk_menu_item_detacher (GtkWidget *widget, g_return_if_fail (priv->submenu == (GtkWidget*) menu); priv->submenu = NULL; + + if (priv->arrow_node) + { + gtk_css_node_set_parent (priv->arrow_node, NULL); + priv->arrow_node = NULL; + } } static void @@ -979,9 +995,6 @@ gtk_menu_item_real_get_height (GtkWidget *widget, gboolean wide_separators; gint separator_height; - gtk_style_context_save (context); - gtk_style_context_add_class (context, GTK_STYLE_CLASS_SEPARATOR); - gtk_widget_style_get (widget, "wide-separators", &wide_separators, "separator-height", &separator_height, @@ -1002,8 +1015,6 @@ gtk_menu_item_real_get_height (GtkWidget *widget, if (nat_height % 2 == 0) nat_height += 1; } - - gtk_style_context_restore (context); } accel_width = 0; @@ -1306,6 +1317,59 @@ gtk_menu_item_set_use_action_appearance (GtkMenuItem *menu_item, } } +static void +node_style_changed_cb (GtkCssNode *node, + GtkCssStyle *old_style, + GtkCssStyle *new_style, + GtkWidget *widget) +{ + GtkBitmask *changes; + static GtkBitmask *affects_size = NULL; + + if (G_UNLIKELY (affects_size == NULL)) + affects_size = _gtk_css_style_property_get_mask_affecting (GTK_CSS_AFFECTS_SIZE | GTK_CSS_AFFECTS_CLIP); + + changes = _gtk_bitmask_new (); + changes = gtk_css_style_add_difference (changes, old_style, new_style); + + if (_gtk_bitmask_intersects (changes, affects_size)) + gtk_widget_queue_resize (widget); + else + gtk_widget_queue_draw (widget); + + _gtk_bitmask_free (changes); +} + +static void +update_node_classes (GtkMenuItem *menu_item) +{ + GtkMenuItemPrivate *priv = menu_item->priv; + GtkCssNode *widget_node, *node; + + if (!priv->arrow_node) + return; + + widget_node = gtk_widget_get_css_node (GTK_WIDGET (menu_item)); + + if (gtk_widget_get_direction (GTK_WIDGET (menu_item)) == GTK_TEXT_DIR_RTL) + { + gtk_css_node_add_class (priv->arrow_node, g_quark_from_static_string (GTK_STYLE_CLASS_LEFT)); + gtk_css_node_remove_class (priv->arrow_node, g_quark_from_static_string (GTK_STYLE_CLASS_RIGHT)); + + node = gtk_css_node_get_first_child (widget_node); + if (node != priv->arrow_node) + gtk_css_node_insert_before (widget_node, priv->arrow_node, node); + } + else + { + gtk_css_node_remove_class (priv->arrow_node, g_quark_from_static_string (GTK_STYLE_CLASS_LEFT)); + gtk_css_node_add_class (priv->arrow_node, g_quark_from_static_string (GTK_STYLE_CLASS_RIGHT)); + + node = gtk_css_node_get_last_child (widget_node); + if (node != priv->arrow_node) + gtk_css_node_insert_after (widget_node, priv->arrow_node, node); + } +} /** * gtk_menu_item_set_submenu: @@ -1335,6 +1399,22 @@ gtk_menu_item_set_submenu (GtkMenuItem *menu_item, gtk_menu_attach_to_widget (GTK_MENU (submenu), GTK_WIDGET (menu_item), gtk_menu_item_detacher); + + if (!GTK_IS_MENU_BAR (gtk_widget_get_parent (GTK_WIDGET (menu_item)))) + { + GtkCssNode *widget_node; + + widget_node = gtk_widget_get_css_node (GTK_WIDGET (menu_item)); + priv->arrow_node = gtk_css_node_new (); + gtk_css_node_set_name (priv->arrow_node, I_("arrow")); + gtk_css_node_set_parent (priv->arrow_node, widget_node); + gtk_css_node_set_state (priv->arrow_node, gtk_css_node_get_state (widget_node)); + g_signal_connect_object (priv->arrow_node, "style-changed", G_CALLBACK (node_style_changed_cb), menu_item, 0); + + update_node_classes (menu_item); + + g_object_unref (priv->arrow_node); + } } if (gtk_widget_get_parent (GTK_WIDGET (menu_item))) @@ -1672,8 +1752,7 @@ gtk_menu_item_draw (GtkWidget *widget, GtkTextDirection direction; gdouble angle; - gtk_style_context_save (context); - gtk_style_context_add_class (context, GTK_STYLE_CLASS_ARROW); + gtk_style_context_save_to_node (context, priv->arrow_node); direction = gtk_widget_get_direction (widget); get_arrow_size (widget, child, &arrow_size, NULL); @@ -1700,12 +1779,9 @@ gtk_menu_item_draw (GtkWidget *widget, gboolean wide_separators; gint separator_height; - gtk_style_context_save (context); - gtk_style_context_add_class (context, GTK_STYLE_CLASS_SEPARATOR); - gtk_widget_style_get (widget, - "wide-separators", &wide_separators, - "separator-height", &separator_height, + "wide-separators", &wide_separators, + "separator-height", &separator_height, NULL); if (wide_separators) gtk_render_frame (context, cr, @@ -1719,8 +1795,6 @@ gtk_menu_item_draw (GtkWidget *widget, y + padding.top, x + w - padding.right - 1, y + padding.top); - - gtk_style_context_restore (context); } GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->draw (widget, cr); @@ -2343,6 +2417,17 @@ gtk_menu_item_parent_set (GtkWidget *widget, GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set (widget, previous_parent); } +static void +gtk_menu_item_direction_changed (GtkWidget *widget, + GtkTextDirection previous_dir) +{ + GtkMenuItem *menu_item = GTK_MENU_ITEM (widget); + + update_node_classes (menu_item); + + GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->direction_changed (widget, previous_dir); +} + void _gtk_menu_item_refresh_accel_path (GtkMenuItem *menu_item, const gchar *prefix, diff --git a/gtk/gtkmenuitemprivate.h b/gtk/gtkmenuitemprivate.h index e0f12a65a5..c786fb098a 100644 --- a/gtk/gtkmenuitemprivate.h +++ b/gtk/gtkmenuitemprivate.h @@ -21,6 +21,7 @@ #include <gtk/gtkmenuitem.h> #include <gtk/deprecated/gtkaction.h> #include <gtk/gtkactionhelper.h> +#include <gtk/gtkcssnodeprivate.h> G_BEGIN_DECLS @@ -39,6 +40,8 @@ struct _GtkMenuItemPrivate GtkAction *action; GtkActionHelper *action_helper; + GtkCssNode *arrow_node; + guint submenu_placement : 1; guint submenu_direction : 1; guint right_justify : 1; diff --git a/gtk/gtkradiomenuitem.c b/gtk/gtkradiomenuitem.c index 27320f0078..9438ed8fbd 100644 --- a/gtk/gtkradiomenuitem.c +++ b/gtk/gtkradiomenuitem.c @@ -61,6 +61,11 @@ * gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE); * } * ]| + * + * # CSS nodes + * + * GtkRadioMenuItem has a main CSS node with name menuitem, and a subnode + * with name radio, which gets the .left or .right style class. */ struct _GtkRadioMenuItemPrivate diff --git a/gtk/gtkseparatormenuitem.c b/gtk/gtkseparatormenuitem.c index 938ca21518..80941a1aa2 100644 --- a/gtk/gtkseparatormenuitem.c +++ b/gtk/gtkseparatormenuitem.c @@ -36,6 +36,10 @@ * The #GtkSeparatorMenuItem is a separator used to group * items within a menu. It displays a horizontal line with a shadow to * make it appear sunken into the interface. + * + * # CSS nodes + * + * GtkSeparatorMenuItem has a single CSS node with name separator. */ G_DEFINE_TYPE (GtkSeparatorMenuItem, gtk_separator_menu_item, GTK_TYPE_MENU_ITEM) @@ -46,9 +50,10 @@ gtk_separator_menu_item_class_init (GtkSeparatorMenuItemClass *class) GTK_CONTAINER_CLASS (class)->child_type = NULL; gtk_widget_class_set_accessible_role (GTK_WIDGET_CLASS (class), ATK_ROLE_SEPARATOR); + gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (class), "separator"); } -static void +static void gtk_separator_menu_item_init (GtkSeparatorMenuItem *item) { } |