mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-10 02:40:11 +00:00
Add nesting popover menus
Add a variant of popover menus that are nesting like traditional menus. This is a better fit for replacing traditional main menus.
This commit is contained in:
parent
c75a368bab
commit
ea44eade21
@ -1787,6 +1787,8 @@ gtk_map_list_model_get_type
|
||||
GtkMenu
|
||||
gtk_menu_new
|
||||
gtk_menu_new_from_model
|
||||
GtkPopoverMenuFlags
|
||||
gtk_menu_new_from_model_full
|
||||
gtk_menu_reorder_child
|
||||
gtk_menu_popup_at_rect
|
||||
gtk_menu_popup_at_widget
|
||||
|
@ -38,18 +38,19 @@ typedef GtkBoxClass GtkMenuSectionBoxClass;
|
||||
|
||||
struct _GtkMenuSectionBox
|
||||
{
|
||||
GtkBox parent_instance;
|
||||
GtkBox parent_instance;
|
||||
|
||||
GtkMenuSectionBox *toplevel;
|
||||
GtkMenuTracker *tracker;
|
||||
GtkBox *item_box;
|
||||
GtkWidget *separator;
|
||||
guint separator_sync_idle;
|
||||
gboolean iconic;
|
||||
gboolean inline_buttons;
|
||||
gboolean circular;
|
||||
gint depth;
|
||||
GtkSizeGroup *indicators;
|
||||
GtkMenuSectionBox *toplevel;
|
||||
GtkMenuTracker *tracker;
|
||||
GtkBox *item_box;
|
||||
GtkWidget *separator;
|
||||
guint separator_sync_idle;
|
||||
gboolean iconic;
|
||||
gboolean inline_buttons;
|
||||
gboolean circular;
|
||||
gint depth;
|
||||
GtkPopoverMenuFlags flags;
|
||||
GtkSizeGroup *indicators;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
@ -295,22 +296,43 @@ gtk_menu_section_box_insert_func (GtkMenuTrackerItem *item,
|
||||
}
|
||||
else if (gtk_menu_tracker_item_get_has_link (item, G_MENU_LINK_SUBMENU))
|
||||
{
|
||||
GtkWidget *stack = NULL;
|
||||
GtkWidget *parent = NULL;
|
||||
gchar *name;
|
||||
if (box->flags & GTK_POPOVER_MENU_NESTED)
|
||||
{
|
||||
GMenuModel *model;
|
||||
GtkWidget *submenu;
|
||||
|
||||
widget = g_object_new (GTK_TYPE_MODEL_BUTTON,
|
||||
"menu-name", gtk_menu_tracker_item_get_label (item),
|
||||
"indicator-size-group", box->indicators,
|
||||
NULL);
|
||||
g_object_bind_property (item, "label", widget, "text", G_BINDING_SYNC_CREATE);
|
||||
g_object_bind_property (item, "icon", widget, "icon", G_BINDING_SYNC_CREATE);
|
||||
g_object_bind_property (item, "sensitive", widget, "sensitive", G_BINDING_SYNC_CREATE);
|
||||
model = _gtk_menu_tracker_item_get_link (item, G_MENU_LINK_SUBMENU);
|
||||
|
||||
get_ancestors (GTK_WIDGET (box->toplevel), GTK_TYPE_STACK, &stack, &parent);
|
||||
g_object_get (gtk_stack_get_page (GTK_STACK (stack), parent), "name", &name, NULL);
|
||||
gtk_menu_section_box_new_submenu (item, box->toplevel, widget, name);
|
||||
g_free (name);
|
||||
submenu = gtk_popover_menu_new_from_model_full (NULL, model, box->flags);
|
||||
gtk_popover_set_has_arrow (GTK_POPOVER (submenu), FALSE);
|
||||
gtk_widget_set_valign (submenu, GTK_ALIGN_START);
|
||||
|
||||
widget = g_object_new (GTK_TYPE_MODEL_BUTTON,
|
||||
"popover", submenu,
|
||||
NULL);
|
||||
g_object_bind_property (item, "label", widget, "text", G_BINDING_SYNC_CREATE);
|
||||
g_object_bind_property (item, "icon", widget, "icon", G_BINDING_SYNC_CREATE);
|
||||
g_object_bind_property (item, "sensitive", widget, "sensitive", G_BINDING_SYNC_CREATE);
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkWidget *stack = NULL;
|
||||
GtkWidget *parent = NULL;
|
||||
gchar *name;
|
||||
|
||||
widget = g_object_new (GTK_TYPE_MODEL_BUTTON,
|
||||
"menu-name", gtk_menu_tracker_item_get_label (item),
|
||||
"indicator-size-group", box->indicators,
|
||||
NULL);
|
||||
g_object_bind_property (item, "label", widget, "text", G_BINDING_SYNC_CREATE);
|
||||
g_object_bind_property (item, "icon", widget, "icon", G_BINDING_SYNC_CREATE);
|
||||
g_object_bind_property (item, "sensitive", widget, "sensitive", G_BINDING_SYNC_CREATE);
|
||||
|
||||
get_ancestors (GTK_WIDGET (box->toplevel), GTK_TYPE_STACK, &stack, &parent);
|
||||
g_object_get (gtk_stack_get_page (GTK_STACK (stack), parent), "name", &name, NULL);
|
||||
gtk_menu_section_box_new_submenu (item, box->toplevel, widget, name);
|
||||
g_free (name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -453,13 +475,15 @@ update_popover_position_cb (GObject *source,
|
||||
}
|
||||
|
||||
void
|
||||
gtk_menu_section_box_new_toplevel (GtkPopoverMenu *popover,
|
||||
GMenuModel *model)
|
||||
gtk_menu_section_box_new_toplevel (GtkPopoverMenu *popover,
|
||||
GMenuModel *model,
|
||||
GtkPopoverMenuFlags flags)
|
||||
{
|
||||
GtkMenuSectionBox *box;
|
||||
|
||||
box = g_object_new (GTK_TYPE_MENU_SECTION_BOX, NULL);
|
||||
box->indicators = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
|
||||
box->flags = flags;
|
||||
|
||||
gtk_popover_menu_add_submenu (popover, GTK_WIDGET (box), "main");
|
||||
|
||||
@ -482,6 +506,7 @@ gtk_menu_section_box_new_submenu (GtkMenuTrackerItem *item,
|
||||
|
||||
box = g_object_new (GTK_TYPE_MENU_SECTION_BOX, NULL);
|
||||
box->indicators = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
|
||||
box->flags = toplevel->flags;
|
||||
|
||||
button = g_object_new (GTK_TYPE_MODEL_BUTTON,
|
||||
"menu-name", name,
|
||||
@ -521,6 +546,7 @@ gtk_menu_section_box_new_section (GtkMenuTrackerItem *item,
|
||||
box->indicators = g_object_ref (parent->indicators);
|
||||
box->toplevel = parent->toplevel;
|
||||
box->depth = parent->depth + 1;
|
||||
box->flags = parent->flags;
|
||||
|
||||
label = gtk_menu_tracker_item_get_label (item);
|
||||
hint = gtk_menu_tracker_item_get_display_hint (item);
|
||||
|
@ -41,8 +41,9 @@ G_BEGIN_DECLS
|
||||
typedef struct _GtkMenuSectionBox GtkMenuSectionBox;
|
||||
|
||||
GType gtk_menu_section_box_get_type (void) G_GNUC_CONST;
|
||||
void gtk_menu_section_box_new_toplevel (GtkPopoverMenu *popover,
|
||||
GMenuModel *model);
|
||||
void gtk_menu_section_box_new_toplevel (GtkPopoverMenu *popover,
|
||||
GMenuModel *model,
|
||||
GtkPopoverMenuFlags flags);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -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");
|
||||
@ -519,6 +523,28 @@ gtk_model_button_set_iconic (GtkModelButton *button,
|
||||
g_object_notify_by_pspec (G_OBJECT (button), properties[PROP_ICONIC]);
|
||||
}
|
||||
|
||||
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:
|
||||
*
|
||||
@ -1129,6 +1206,53 @@ gtk_model_button_class_init (GtkModelButtonClass *class)
|
||||
gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (class), I_("modelbutton"));
|
||||
}
|
||||
|
||||
static void
|
||||
close_submenus (GtkPopover *popover)
|
||||
{
|
||||
GtkPopoverMenu *menu;
|
||||
|
||||
if (GTK_IS_POPOVER_MENU (popover))
|
||||
{
|
||||
GtkWidget *submenu;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
enter_cb (GtkEventController *controller,
|
||||
double x,
|
||||
@ -1151,30 +1275,10 @@ enter_cb (GtkEventController *controller,
|
||||
NULL);
|
||||
|
||||
if (popover && (is || contains))
|
||||
gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), target);
|
||||
}
|
||||
|
||||
static void
|
||||
leave_cb (GtkEventController *controller,
|
||||
GdkCrossingMode mode,
|
||||
GdkNotifyType type,
|
||||
gpointer data)
|
||||
{
|
||||
GtkWidget *target;
|
||||
GtkWidget *popover;
|
||||
gboolean is;
|
||||
gboolean contains;
|
||||
|
||||
target = gtk_event_controller_get_widget (controller);
|
||||
popover = gtk_widget_get_ancestor (target, GTK_TYPE_POPOVER_MENU);
|
||||
|
||||
g_object_get (controller,
|
||||
"is-pointer-focus", &is,
|
||||
"contains-pointer-focus", &contains,
|
||||
NULL);
|
||||
|
||||
if (popover && !(is || contains))
|
||||
gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), NULL);
|
||||
{
|
||||
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 ();
|
||||
|
@ -107,6 +107,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkpopoverprivate.h"
|
||||
#include "gtkpopovermenuprivate.h"
|
||||
#include "gtknative.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkeventcontrollerkey.h"
|
||||
@ -398,6 +399,19 @@ gtk_popover_focus_out (GtkWidget *widget)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
close_menu (GtkPopover *popover)
|
||||
{
|
||||
while (popover)
|
||||
{
|
||||
gtk_popover_popdown (popover);
|
||||
if (GTK_IS_POPOVER_MENU (popover))
|
||||
popover = (GtkPopover *)gtk_popover_menu_get_parent_menu (GTK_POPOVER_MENU (popover));
|
||||
else
|
||||
popover = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_popover_key_pressed (GtkWidget *widget,
|
||||
guint keyval,
|
||||
@ -406,7 +420,7 @@ gtk_popover_key_pressed (GtkWidget *widget,
|
||||
{
|
||||
if (keyval == GDK_KEY_Escape)
|
||||
{
|
||||
gtk_popover_popdown (GTK_POPOVER (widget));
|
||||
close_menu (GTK_POPOVER (widget));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -29,9 +29,12 @@
|
||||
#include "gtkpopoverprivate.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkeventcontrollerkey.h"
|
||||
#include "gtkeventcontrollermotion.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtkbindings.h"
|
||||
#include "gtkmodelbutton.h"
|
||||
#include "gtkpopovermenubar.h"
|
||||
|
||||
|
||||
/**
|
||||
@ -124,6 +127,8 @@ struct _GtkPopoverMenu
|
||||
GtkPopover parent_instance;
|
||||
|
||||
GtkWidget *active_item;
|
||||
GtkWidget *open_submenu;
|
||||
GtkWidget *parent_menu;
|
||||
};
|
||||
|
||||
struct _GtkPopoverMenuClass
|
||||
@ -137,6 +142,38 @@ enum {
|
||||
|
||||
G_DEFINE_TYPE (GtkPopoverMenu, gtk_popover_menu, GTK_TYPE_POPOVER)
|
||||
|
||||
GtkWidget *
|
||||
gtk_popover_menu_get_parent_menu (GtkPopoverMenu *menu)
|
||||
{
|
||||
return menu->parent_menu;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_popover_menu_set_parent_menu (GtkPopoverMenu *menu,
|
||||
GtkWidget *parent)
|
||||
{
|
||||
menu->parent_menu = parent;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_popover_menu_get_open_submenu (GtkPopoverMenu *menu)
|
||||
{
|
||||
return menu->open_submenu;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_popover_menu_set_open_submenu (GtkPopoverMenu *menu,
|
||||
GtkWidget *submenu)
|
||||
{
|
||||
menu->open_submenu = submenu;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_popover_menu_get_active_item (GtkPopoverMenu *menu)
|
||||
{
|
||||
return menu->active_item;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_popover_menu_set_active_item (GtkPopoverMenu *menu,
|
||||
GtkWidget *item)
|
||||
@ -150,8 +187,16 @@ gtk_popover_menu_set_active_item (GtkPopoverMenu *menu,
|
||||
|
||||
if (menu->active_item)
|
||||
{
|
||||
GtkWidget *popover;
|
||||
|
||||
gtk_widget_set_state_flags (menu->active_item, GTK_STATE_FLAG_SELECTED, FALSE);
|
||||
gtk_widget_grab_focus (menu->active_item);
|
||||
if (GTK_IS_MODEL_BUTTON (item))
|
||||
g_object_get (item, "popover", &popover, NULL);
|
||||
|
||||
if (!popover || popover != menu->open_submenu)
|
||||
gtk_widget_grab_focus (menu->active_item);
|
||||
|
||||
g_clear_object (&popover);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -168,14 +213,40 @@ static void
|
||||
focus_out (GtkEventController *controller,
|
||||
GdkCrossingMode mode,
|
||||
GdkNotifyType detail,
|
||||
GtkPopover *popover)
|
||||
GtkPopoverMenu *menu)
|
||||
{
|
||||
gboolean contains_focus;
|
||||
|
||||
g_object_get (controller, "contains-focus", &contains_focus, NULL);
|
||||
|
||||
if (!contains_focus)
|
||||
gtk_popover_popdown (popover);
|
||||
{
|
||||
if (menu->parent_menu &&
|
||||
GTK_POPOVER_MENU (menu->parent_menu)->open_submenu == (GtkWidget*)menu)
|
||||
GTK_POPOVER_MENU (menu->parent_menu)->open_submenu = NULL;
|
||||
gtk_popover_popdown (GTK_POPOVER (menu));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
leave_cb (GtkEventController *controller,
|
||||
GdkCrossingMode mode,
|
||||
GdkNotifyType type,
|
||||
gpointer data)
|
||||
{
|
||||
GtkWidget *target;
|
||||
gboolean is;
|
||||
gboolean contains;
|
||||
|
||||
target = gtk_event_controller_get_widget (controller);
|
||||
|
||||
g_object_get (controller,
|
||||
"is-pointer-focus", &is,
|
||||
"contains-pointer-focus", &contains,
|
||||
NULL);
|
||||
|
||||
if (!(is || contains))
|
||||
gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (target), NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -199,6 +270,11 @@ gtk_popover_menu_init (GtkPopoverMenu *popover)
|
||||
controller = gtk_event_controller_key_new ();
|
||||
g_signal_connect (controller, "focus-out", G_CALLBACK (focus_out), popover);
|
||||
gtk_widget_add_controller (GTK_WIDGET (popover), controller);
|
||||
|
||||
controller = gtk_event_controller_motion_new ();
|
||||
g_signal_connect (controller, "leave", G_CALLBACK (leave_cb), popover);
|
||||
gtk_widget_add_controller (GTK_WIDGET (popover), controller);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
@ -310,10 +386,35 @@ gtk_popover_menu_focus (GtkWidget *widget,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GTK_POPOVER_MENU (widget)->open_submenu)
|
||||
{
|
||||
if (gtk_widget_child_focus (GTK_POPOVER_MENU (widget)->open_submenu, direction))
|
||||
return TRUE;
|
||||
if (direction == GTK_DIR_LEFT)
|
||||
{
|
||||
gtk_widget_grab_focus (GTK_POPOVER_MENU (widget)->active_item);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (gtk_widget_focus_move (widget, direction))
|
||||
return TRUE;
|
||||
|
||||
if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN)
|
||||
if (direction == GTK_DIR_LEFT || direction == GTK_DIR_RIGHT)
|
||||
{
|
||||
/* If we are part of a menubar, we want to let the
|
||||
* menubar use left/right arrows for cycling, else
|
||||
* we eat them.
|
||||
*/
|
||||
if (gtk_widget_get_ancestor (widget, GTK_TYPE_POPOVER_MENU_BAR) ||
|
||||
(gtk_popover_menu_get_parent_menu (GTK_POPOVER_MENU (widget)) &&
|
||||
direction == GTK_DIR_LEFT))
|
||||
return FALSE;
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
else if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN)
|
||||
{
|
||||
GtkWidget *p;
|
||||
|
||||
@ -503,11 +604,47 @@ gtk_popover_menu_add_submenu (GtkPopoverMenu *popover,
|
||||
* Actions can also be added using gtk_widget_insert_action_group()
|
||||
* on the menus attach widget or on any of its parent widgets.
|
||||
*
|
||||
* This function creates menus with sliding submenus.
|
||||
* See gtk_popover_menu_new_from_model_full() for a way
|
||||
* to control this.
|
||||
*
|
||||
* Returns: the new #GtkPopoverMenu
|
||||
*/
|
||||
GtkWidget *
|
||||
gtk_popover_menu_new_from_model (GtkWidget *relative_to,
|
||||
GMenuModel *model)
|
||||
|
||||
{
|
||||
return gtk_popover_menu_new_from_model_full (relative_to, model, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_popover_menu_new_from_model_full:
|
||||
* @relative_to: (allow-none): #GtkWidget the popover is related to
|
||||
* @model: a #GMenuModel
|
||||
* @flags: flags that affect how the menu is created
|
||||
*
|
||||
* Creates a #GtkPopoverMenu and populates it according to
|
||||
* @model. The popover is pointed to the @relative_to widget.
|
||||
*
|
||||
* The created buttons are connected to actions found in the
|
||||
* #GtkApplicationWindow to which the popover belongs - typically
|
||||
* by means of being attached to a widget that is contained within
|
||||
* the #GtkApplicationWindows widget hierarchy.
|
||||
*
|
||||
* Actions can also be added using gtk_widget_insert_action_group()
|
||||
* on the menus attach widget or on any of its parent widgets.
|
||||
*
|
||||
* The only flag that is supported currently is
|
||||
* #GTK_POPOVER_MENU_NESTED, which makes GTK create traditional,
|
||||
* nested submenus instead of the default sliding submenus.
|
||||
*
|
||||
* Returns: the new #GtkPopoverMenu
|
||||
*/
|
||||
GtkWidget *
|
||||
gtk_popover_menu_new_from_model_full (GtkWidget *relative_to,
|
||||
GMenuModel *model,
|
||||
GtkPopoverMenuFlags flags)
|
||||
{
|
||||
GtkWidget *popover;
|
||||
|
||||
@ -515,7 +652,8 @@ gtk_popover_menu_new_from_model (GtkWidget *relative_to,
|
||||
g_return_val_if_fail (G_IS_MENU_MODEL (model), NULL);
|
||||
|
||||
popover = gtk_popover_menu_new (relative_to);
|
||||
gtk_menu_section_box_new_toplevel (GTK_POPOVER_MENU (popover), model);
|
||||
gtk_menu_section_box_new_toplevel (GTK_POPOVER_MENU (popover), model, flags);
|
||||
|
||||
return popover;
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,24 @@ GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_popover_menu_new_from_model (GtkWidget *relative_to,
|
||||
GMenuModel *model);
|
||||
|
||||
/**
|
||||
* GtkPopoverMenuFlags:
|
||||
* @GTK_POPOVER_MENU_NESTED: Create submenus as nested
|
||||
* popovers. Without this flag, submenus are created as
|
||||
* sliding pages that replace the main menu.
|
||||
*
|
||||
* Flags that affect how popover menus are created from
|
||||
* a menu model.
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_POPOVER_MENU_NESTED = 1 << 0
|
||||
} GtkPopoverMenuFlags;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_popover_menu_new_from_model_full (GtkWidget *relative_to,
|
||||
GMenuModel *model,
|
||||
GtkPopoverMenuFlags flags);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_popover_menu_add_submenu (GtkPopoverMenu *popover,
|
||||
GtkWidget *submenu,
|
||||
|
@ -314,7 +314,11 @@ gtk_popover_menu_bar_item_size_allocate (GtkWidget *widget,
|
||||
static void
|
||||
gtk_popover_menu_bar_item_activate (GtkPopoverMenuBarItem *item)
|
||||
{
|
||||
gtk_popover_popup (GTK_POPOVER (item->popover));
|
||||
GtkPopoverMenuBar *bar;
|
||||
|
||||
bar = GTK_POPOVER_MENU_BAR (gtk_widget_get_ancestor (GTK_WIDGET (item), GTK_TYPE_POPOVER_MENU_BAR));
|
||||
|
||||
set_active_item (bar, item, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -22,8 +22,15 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gtk_popover_menu_set_active_item (GtkPopoverMenu *popover,
|
||||
GtkWidget *item);
|
||||
GtkWidget *gtk_popover_menu_get_active_item (GtkPopoverMenu *menu);
|
||||
void gtk_popover_menu_set_active_item (GtkPopoverMenu *menu,
|
||||
GtkWidget *item);
|
||||
GtkWidget *gtk_popover_menu_get_open_submenu (GtkPopoverMenu *menu);
|
||||
void gtk_popover_menu_set_open_submenu (GtkPopoverMenu *menu,
|
||||
GtkWidget *submenu);
|
||||
GtkWidget *gtk_popover_menu_get_parent_menu (GtkPopoverMenu *menu);
|
||||
void gtk_popover_menu_set_parent_menu (GtkPopoverMenu *menu,
|
||||
GtkWidget *parent);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
<interface>
|
||||
<object class="GtkPopoverMenu" id="popover">
|
||||
<property name="autohide">True</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
@ -105,14 +106,14 @@
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkModelButton">
|
||||
<property name="text">Submenu 1</property>
|
||||
<property name="menu-name">submenu1</property>
|
||||
<property name="label">Submenu 1</property>
|
||||
<property name="popover">submenu1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkModelButton">
|
||||
<property name="text">Submenu 2</property>
|
||||
<property name="menu-name">submenu2</property>
|
||||
<property name="label">Submenu 2</property>
|
||||
<property name="popover">submenu2</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
@ -151,17 +152,13 @@
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkPopoverMenu" id="submenu1">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="name">submenu1</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="margin">10</property>
|
||||
<child>
|
||||
<object class="GtkModelButton">
|
||||
<property name="text">Submenu 1</property>
|
||||
<property name="menu-name">main</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
@ -352,17 +349,13 @@
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkPopoverMenu" id="submenu2">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="name">submenu2</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="margin">10</property>
|
||||
<child>
|
||||
<object class="GtkModelButton">
|
||||
<property name="text">Submenu 2</property>
|
||||
<property name="menu-name">main</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkModelButton">
|
||||
<property name="action-name">top.action7</property>
|
||||
@ -377,24 +370,19 @@
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkModelButton">
|
||||
<property name="text">Subsubmenu</property>
|
||||
<property name="icon">icon9</property>
|
||||
<property name="menu-name">subsubmenu</property>
|
||||
<property name="label">Subsubmenu</property>
|
||||
<property name="popover">subsubmenu</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkPopoverMenu" id="subsubmenu">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="name">subsubmenu</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="margin">10</property>
|
||||
<child>
|
||||
<object class="GtkModelButton">
|
||||
<property name="text">Subsubmenu</property>
|
||||
<property name="menu-name">submenu2</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkModelButton">
|
||||
<property name="action-name">action8</property>
|
||||
|
@ -37,6 +37,7 @@ main (int argc, char *argv[])
|
||||
GtkWidget *win;
|
||||
GtkWidget *box;
|
||||
GtkWidget *button;
|
||||
GtkWidget *button1;
|
||||
GtkWidget *button2;
|
||||
GtkBuilder *builder;
|
||||
GMenuModel *model;
|
||||
@ -44,6 +45,7 @@ main (int argc, char *argv[])
|
||||
GtkWidget *overlay;
|
||||
GtkWidget *grid;
|
||||
GtkWidget *popover;
|
||||
GtkWidget *popover1;
|
||||
GtkWidget *popover2;
|
||||
GtkWidget *label;
|
||||
GtkWidget *check;
|
||||
@ -93,6 +95,8 @@ main (int argc, char *argv[])
|
||||
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
button = gtk_menu_button_new ();
|
||||
gtk_container_add (GTK_CONTAINER (box), button);
|
||||
button1 = gtk_menu_button_new ();
|
||||
gtk_container_add (GTK_CONTAINER (box), button1);
|
||||
button2 = gtk_menu_button_new ();
|
||||
gtk_container_add (GTK_CONTAINER (box), button2);
|
||||
|
||||
@ -100,6 +104,9 @@ main (int argc, char *argv[])
|
||||
gtk_menu_button_set_use_popover (GTK_MENU_BUTTON (button), TRUE);
|
||||
popover = GTK_WIDGET (gtk_menu_button_get_popover (GTK_MENU_BUTTON (button)));
|
||||
|
||||
popover1 = gtk_popover_menu_new_from_model_full (NULL, model, GTK_POPOVER_MENU_NESTED);
|
||||
gtk_menu_button_set_popover (GTK_MENU_BUTTON (button1), popover1);
|
||||
|
||||
builder = gtk_builder_new_from_file ("popover2.ui");
|
||||
popover2 = (GtkWidget *)gtk_builder_get_object (builder, "popover");
|
||||
gtk_menu_button_set_popover (GTK_MENU_BUTTON (button2), popover2);
|
||||
@ -110,6 +117,7 @@ main (int argc, char *argv[])
|
||||
label = gtk_label_new ("Popover hexpand");
|
||||
check = gtk_check_button_new ();
|
||||
g_object_bind_property (check, "active", popover, "hexpand", G_BINDING_SYNC_CREATE);
|
||||
g_object_bind_property (check, "active", popover1, "hexpand", G_BINDING_SYNC_CREATE);
|
||||
g_object_bind_property (check, "active", popover2, "hexpand", G_BINDING_SYNC_CREATE);
|
||||
gtk_grid_attach (GTK_GRID (grid), label , 1, 1, 1, 1);
|
||||
gtk_grid_attach (GTK_GRID (grid), check, 2, 1, 1, 1);
|
||||
@ -117,6 +125,7 @@ main (int argc, char *argv[])
|
||||
label = gtk_label_new ("Popover vexpand");
|
||||
check = gtk_check_button_new ();
|
||||
g_object_bind_property (check, "active", popover, "vexpand", G_BINDING_SYNC_CREATE);
|
||||
g_object_bind_property (check, "active", popover1, "vexpand", G_BINDING_SYNC_CREATE);
|
||||
g_object_bind_property (check, "active", popover2, "vexpand", G_BINDING_SYNC_CREATE);
|
||||
gtk_grid_attach (GTK_GRID (grid), label , 1, 2, 1, 1);
|
||||
gtk_grid_attach (GTK_GRID (grid), check, 2, 2, 1, 1);
|
||||
@ -129,6 +138,7 @@ main (int argc, char *argv[])
|
||||
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "right", "Right");
|
||||
gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 1);
|
||||
g_object_bind_property (combo, "active", button, "direction", G_BINDING_SYNC_CREATE);
|
||||
g_object_bind_property (combo, "active", button1, "direction", G_BINDING_SYNC_CREATE);
|
||||
g_object_bind_property (combo, "active", button2, "direction", G_BINDING_SYNC_CREATE);
|
||||
gtk_grid_attach (GTK_GRID (grid), label , 1, 3, 1, 1);
|
||||
gtk_grid_attach (GTK_GRID (grid), combo, 2, 3, 1, 1);
|
||||
|
Loading…
Reference in New Issue
Block a user