diff options
Diffstat (limited to 'gtk/gtkmodelbutton.c')
-rw-r--r-- | gtk/gtkmodelbutton.c | 167 |
1 files changed, 135 insertions, 32 deletions
diff --git a/gtk/gtkmodelbutton.c b/gtk/gtkmodelbutton.c index 80552ec33b..98d70e2226 100644 --- a/gtk/gtkmodelbutton.c +++ b/gtk/gtkmodelbutton.c @@ -43,6 +43,7 @@ #include "gtkactionable.h" #include "gtkeventcontrollermotion.h" #include "gtkeventcontrollerkey.h" +#include "gtknative.h" /** * SECTION:gtkmodelbutton @@ -162,6 +163,7 @@ struct _GtkModelButton GtkWidget *start_indicator; GtkWidget *end_box; GtkWidget *end_indicator; + GtkWidget *popover; gboolean active; gboolean centered; gboolean iconic; @@ -184,6 +186,7 @@ enum PROP_USE_MARKUP, PROP_ACTIVE, PROP_MENU_NAME, + PROP_POPOVER, PROP_ICONIC, PROP_ACCEL, PROP_INDICATOR_SIZE_GROUP, @@ -275,7 +278,8 @@ gtk_model_button_update_state (GtkModelButton *button) case GTK_BUTTON_ROLE_NORMAL: start_type = GTK_CSS_IMAGE_BUILTIN_NONE; - if (button->menu_name != NULL) + if (button->menu_name != NULL || + button->popover != NULL) end_type = GTK_CSS_IMAGE_BUILTIN_ARROW_RIGHT; else end_type = GTK_CSS_IMAGE_BUILTIN_NONE; @@ -344,7 +348,7 @@ update_node_name (GtkModelButton *button) case GTK_BUTTON_ROLE_NORMAL: a11y_role = ATK_ROLE_PUSH_BUTTON; start_name = I_("none"); - if (button->menu_name) + if (button->menu_name || button->popover) end_name = I_("arrow"); else end_name = I_("none"); @@ -520,6 +524,28 @@ gtk_model_button_set_iconic (GtkModelButton *button, } static void +gtk_model_button_set_popover (GtkModelButton *button, + GtkWidget *popover) +{ + if (button->popover) + gtk_popover_set_relative_to (GTK_POPOVER (button->popover), NULL); + + button->popover = popover; + + if (button->popover) + { + gtk_popover_set_relative_to (GTK_POPOVER (button->popover), GTK_WIDGET (button)); + gtk_popover_set_position (GTK_POPOVER (button->popover), GTK_POS_RIGHT); + } + + update_node_name (button); + gtk_model_button_update_state (button); + + gtk_widget_queue_resize (GTK_WIDGET (button)); + g_object_notify_by_pspec (G_OBJECT (button), properties[PROP_POPOVER]); +} + +static void update_accel (GtkModelButton *button, const char *accel) { @@ -591,6 +617,10 @@ gtk_model_button_get_property (GObject *object, g_value_set_string (value, button->menu_name); break; + case PROP_POPOVER: + g_value_set_object (value, button->popover); + break; + case PROP_ICONIC: g_value_set_boolean (value, button->iconic); break; @@ -643,6 +673,10 @@ gtk_model_button_set_property (GObject *object, gtk_model_button_set_menu_name (button, g_value_get_string (value)); break; + case PROP_POPOVER: + gtk_model_button_set_popover (button, (GtkWidget *)g_value_get_object (value)); + break; + case PROP_ICONIC: gtk_model_button_set_iconic (button, g_value_get_boolean (value)); break; @@ -783,7 +817,9 @@ gtk_model_button_size_allocate (GtkWidget *widget, int height, int baseline) { - if (GTK_MODEL_BUTTON (widget)->iconic) + GtkModelButton *button = GTK_MODEL_BUTTON (widget); + + if (button->iconic) { GTK_WIDGET_CLASS (gtk_model_button_parent_class)->size_allocate (widget, width, @@ -792,14 +828,12 @@ gtk_model_button_size_allocate (GtkWidget *widget, } else { - GtkModelButton *button; GtkAllocation child_allocation; GtkWidget *child; int start_width, start_height; int end_width, end_height; int min; - button = GTK_MODEL_BUTTON (widget); child = gtk_bin_get_child (GTK_BIN (widget)); gtk_widget_measure (button->start_box, @@ -860,6 +894,9 @@ gtk_model_button_size_allocate (GtkWidget *widget, gtk_widget_size_allocate (child, &child_allocation, baseline); } } + + if (button->popover) + gtk_native_check_resize (GTK_NATIVE (button->popover)); } static void @@ -888,10 +925,18 @@ close_menu (GtkModelButton *button) GtkWidget *popover; popover = gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_POPOVER); - if (popover != NULL) - gtk_popover_popdown (GTK_POPOVER (popover)); + while (popover != NULL) + { + gtk_popover_popdown (GTK_POPOVER (popover)); + if (GTK_IS_POPOVER_MENU (popover)) + popover = gtk_popover_menu_get_parent_menu (GTK_POPOVER_MENU (popover)); + else + popover = NULL; + } } +static void open_submenu (GtkPopover *popover); + static void gtk_model_button_clicked (GtkButton *button) { @@ -901,6 +946,17 @@ gtk_model_button_clicked (GtkButton *button) { switch_menu (model_button); } + else if (model_button->popover != NULL) + { + GtkPopoverMenu *menu; + GtkWidget *submenu; + + menu = (GtkPopoverMenu *)gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_POPOVER_MENU); + submenu = model_button->popover; + gtk_popover_popup (GTK_POPOVER (submenu)); + gtk_popover_menu_set_open_submenu (menu, submenu); + gtk_popover_menu_set_parent_menu (GTK_POPOVER_MENU (submenu), GTK_WIDGET (menu)); + } else if (model_button->role == GTK_BUTTON_ROLE_NORMAL) { close_menu (model_button); @@ -915,6 +971,7 @@ gtk_model_button_finalize (GObject *object) gtk_widget_unparent (button->start_box); gtk_widget_unparent (button->end_box); g_free (button->accel); + g_clear_pointer (&button->popover, gtk_widget_unparent); G_OBJECT_CLASS (gtk_model_button_parent_class)->finalize (object); } @@ -979,6 +1036,20 @@ gtk_model_button_focus (GtkWidget *widget, switch_menu (button); return TRUE; } + else if (direction == GTK_DIR_RIGHT && + button->role == GTK_BUTTON_ROLE_NORMAL && + button->popover != NULL) + { + GtkPopoverMenu *menu; + GtkWidget *submenu; + + menu = GTK_POPOVER_MENU (gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_POPOVER_MENU)); + submenu = button->popover; + gtk_popover_popup (GTK_POPOVER (submenu)); + gtk_popover_menu_set_open_submenu (menu, submenu); + gtk_popover_menu_set_parent_menu (GTK_POPOVER_MENU (submenu), GTK_WIDGET (menu)); + return TRUE; + } } else { @@ -1080,8 +1151,7 @@ gtk_model_button_class_init (GtkModelButtonClass *class) /** * GtkModelButton:menu-name: * - * The name of a submenu to open when the button is activated. - * If this is set, the button should not have an action associated with it. + * The name of a submenu to open when the button is activated. * If this is set, the button should not have an action associated with it. */ properties[PROP_MENU_NAME] = g_param_spec_string ("menu-name", @@ -1090,6 +1160,13 @@ gtk_model_button_class_init (GtkModelButtonClass *class) NULL, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + properties[PROP_POPOVER] = + g_param_spec_object ("popover", + P_("Popover"), + P_("Popover to open"), + GTK_TYPE_POPOVER, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + /** * GtkModelButton:iconic: * @@ -1130,32 +1207,56 @@ gtk_model_button_class_init (GtkModelButtonClass *class) } static void -enter_cb (GtkEventController *controller, - double x, - double y, - GdkCrossingMode mode, - GdkNotifyType type, - gpointer data) +close_submenus (GtkPopover *popover) { - GtkWidget *target; - GtkWidget *popover; - gboolean is; - gboolean contains; + GtkPopoverMenu *menu; - target = gtk_event_controller_get_widget (controller); - popover = gtk_widget_get_ancestor (target, GTK_TYPE_POPOVER_MENU); + if (GTK_IS_POPOVER_MENU (popover)) + { + GtkWidget *submenu; - g_object_get (controller, - "is-pointer-focus", &is, - "contains-pointer-focus", &contains, - NULL); + menu = GTK_POPOVER_MENU (popover); + submenu = gtk_popover_menu_get_open_submenu (menu); + if (submenu) + { + close_submenus (GTK_POPOVER (submenu)); + gtk_popover_popdown (GTK_POPOVER (submenu)); + gtk_popover_menu_set_open_submenu (menu, NULL); + } + } +} - if (popover && (is || contains)) - gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), target); +static void +open_submenu (GtkPopover *popover) +{ + if (GTK_IS_POPOVER_MENU (popover)) + { + GtkWidget *active_item; + + active_item = gtk_popover_menu_get_active_item (GTK_POPOVER_MENU (popover)); + if (GTK_IS_MODEL_BUTTON (active_item) && + GTK_MODEL_BUTTON (active_item)->popover) + { + GtkWidget *submenu; + + submenu = GTK_MODEL_BUTTON (active_item)->popover; + if (gtk_popover_menu_get_open_submenu (GTK_POPOVER_MENU (popover)) != submenu) +{ +g_print ("close submenus %p %p\n", gtk_popover_menu_get_open_submenu (GTK_POPOVER_MENU (popover)), submenu); + close_submenus (popover); +} + + gtk_popover_popup (GTK_POPOVER (submenu)); + gtk_popover_menu_set_open_submenu (GTK_POPOVER_MENU (popover), submenu); + gtk_popover_menu_set_parent_menu (GTK_POPOVER_MENU (submenu), GTK_WIDGET (popover)); + } + } } static void -leave_cb (GtkEventController *controller, +enter_cb (GtkEventController *controller, + double x, + double y, GdkCrossingMode mode, GdkNotifyType type, gpointer data) @@ -1173,8 +1274,11 @@ leave_cb (GtkEventController *controller, "contains-pointer-focus", &contains, NULL); - if (popover && !(is || contains)) - gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), NULL); + if (popover && (is || contains)) + { + gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), target); + open_submenu (GTK_POPOVER (popover)); + } } static void @@ -1231,8 +1335,7 @@ gtk_model_button_init (GtkModelButton *button) update_node_ordering (button); controller = gtk_event_controller_motion_new (); - g_signal_connect (controller, "enter", G_CALLBACK (enter_cb), NULL); - g_signal_connect (controller, "leave", G_CALLBACK (leave_cb), NULL); + g_signal_connect (controller, "enter", G_CALLBACK (enter_cb), button); gtk_widget_add_controller (GTK_WIDGET (button), controller); controller = gtk_event_controller_key_new (); |