Compare commits

...

19 Commits

Author SHA1 Message Date
Matthias Clasen
5cf0bff0ba Fix up the last commit 2014-10-27 08:18:02 -04:00
Matthias Clasen
aa90e6fd02 GtkPopoverMenu: Add API to open a submenu
This allows alternative menu switching to be implemented, besides
the builtin GtkModelButton support.
2014-10-26 19:50:07 -04:00
Matthias Clasen
9b8043be93 Add a scale to the popover menu example
Demonsttrate that this is flexible enough for the intended uses.
2014-10-26 19:50:07 -04:00
Matthias Clasen
78585c9b38 Add documentation
Document GtkModelButton and GtkPopoverMenu.
2014-10-26 19:15:18 -04:00
Matthias Clasen
e0f6605d50 Adapt the testpopover example
Add the required boxes for each submenu. We now need to specify the
submenu name only once per submenu.
2014-10-26 19:15:11 -04:00
Matthias Clasen
e068b048b5 GtkPopoverMenu: Simplify
Change things so each submenu is a single child, and users are
required to create the box themselves.
2014-10-26 19:06:33 -04:00
Matthias Clasen
e449ab5fa3 Adapt the testpopover example
Drop all all explicit role setting from the testpopover example.
2014-10-26 19:06:33 -04:00
Matthias Clasen
4589aec1aa GtkModelButton: Don't require an explicit role
When we have an action-name, we can deduce the role from the
action that is looked up by the action helper.
2014-10-26 19:06:33 -04:00
Matthias Clasen
6b27f63008 Adapt the testpopover example 2014-10-26 19:06:32 -04:00
Matthias Clasen
faa793610a GtkModelButton: Rename action-role to role
Rename action-role to role, and make all properties readable.
2014-10-26 19:06:32 -04:00
Matthias Clasen
4d02a65111 GtkModelButton: Drop unused :accel property 2014-10-26 19:06:32 -04:00
Matthias Clasen
c413ddefc1 Adapt the testpopover example
Drop all uses of has-submenu.
2014-10-26 19:06:32 -04:00
Matthias Clasen
7dcda2a17a Drop GtkModelButton:has-submenu
Replace the has-submenu property with checking menu-name != NULL.
2014-10-26 19:06:32 -04:00
Matthias Clasen
5ce97bb77e Adapt the testpopover example
We now use GtkPopoverMenu, and just add the model buttons
directly to it.
2014-10-26 19:06:32 -04:00
Matthias Clasen
fe0aa6e18c GtkModelButton: Add a menu-name property
Add a menu-name property and use it in a default implementation
of ::clicked to switch menus if we are inside a stack. This means
GtkModelButton is no longer entirely generic, but rather expects
to be used inside a GtkPopoverMenu. It still works in other contexts
too, of course.
2014-10-26 19:06:32 -04:00
Matthias Clasen
44ad311eeb Add GtkPopoverMenu
GtkPopoverMenu is a GtkPopover subclass that has a menu-like stack
built-in.
2014-10-26 19:06:25 -04:00
Matthias Clasen
2fe9779b52 Add a manual popover menu example
Reproduce the testpopover popover manually, by packing model
buttons into a stack in a popover.
2014-10-26 15:12:54 -04:00
Matthias Clasen
660bc6adf6 Make GtkModelButton a better GtkActionable
Rename the "toggled" property to "active", since that is what
GtkActionHelper expects to update for check and radio actions.
Also make the property readable, since GtkActionHelper wants
to read it.
2014-10-26 15:12:54 -04:00
Matthias Clasen
70005e0918 Make GtkModelButton public 2014-10-26 15:12:54 -04:00
15 changed files with 1360 additions and 163 deletions

View File

@@ -114,6 +114,7 @@
<xi:include href="xml/gtkscalebutton.xml" />
<xi:include href="xml/gtkvolumebutton.xml" />
<xi:include href="xml/gtklockbutton.xml" />
<xi:include href="xml/gtkmodelbutton.xml" />
</chapter>
<chapter id="NumericEntry">
@@ -191,6 +192,7 @@
<xi:include href="xml/gtktoggletoolbutton.xml" />
<xi:include href="xml/gtkradiotoolbutton.xml" />
<xi:include href="xml/gtkpopover.xml" />
<xi:include href="xml/gtkpopovermenu.xml" />
</chapter>
<chapter id="SelectorWidgets">

View File

@@ -7955,6 +7955,22 @@ gtk_popover_set_modal
gtk_popover_get_modal
</SECTION>
<SECTION>
<FILE>gtkpopovermenu</FILE>
<TITLE>GtkPopoverMenu</TITLE>
GtkPopoverMenu
gtk_popover_menu_new
gtk_popover_menu_open_submenu
</SECTION>
<SECTION>
<FILE>gtkmodelbutton</FILE>
<TITLE>GtkModelButton</TITLE>
GtkModelButton
GtkButtonRole
gtk_model_button_new
</SECTION>
<SECTION>
<FILE>gtkeventcontroller</FILE>
<TITLE>GtkEventController</TITLE>

View File

@@ -126,6 +126,7 @@ gtk_menu_shell_get_type
gtk_menu_tool_button_get_type
gtk_message_dialog_get_type
gtk_misc_get_type
gtk_model_button_get_type
gtk_mount_operation_get_type
gtk_notebook_get_type
gtk_numerable_icon_get_type
@@ -139,6 +140,7 @@ gtk_paper_size_get_type
gtk_places_sidebar_get_type
@ENABLE_ON_X11@gtk_plug_get_type
gtk_popover_get_type
gtk_popover_menu_get_type
@DISABLE_ON_W32@gtk_printer_get_type
gtk_print_context_get_type
@DISABLE_ON_W32@gtk_print_job_get_type

View File

@@ -393,6 +393,7 @@ gtk_public_h_sources = \
gtkmenushell.h \
gtkmenutoolbutton.h \
gtkmessagedialog.h \
gtkmodelbutton.h \
gtkmodules.h \
gtkmountoperation.h \
gtknotebook.h \
@@ -405,6 +406,7 @@ gtk_public_h_sources = \
gtkplacessidebar.h \
gtkplug.h \
gtkpopover.h \
gtkpopovermenu.h \
gtkprintcontext.h \
gtkprintoperation.h \
gtkprintoperationpreview.h \
@@ -624,7 +626,6 @@ gtk_private_h_sources = \
gtkmenutrackeritem.h \
gtkmnemonichash.h \
gtkmodelmenuitem.h \
gtkmodelbutton.h \
gtkmodifierstyle.h \
gtkmodulesprivate.h \
gtkmountoperationprivate.h \
@@ -1008,6 +1009,7 @@ gtk_base_c_sources = \
gtkprogressbar.c \
gtkpixelcache.c \
gtkpopover.c \
gtkpopovermenu.c \
gtkradiobutton.c \
gtkradiomenuitem.c \
gtkradiotoolbutton.c \

View File

@@ -141,6 +141,7 @@
#include <gtk/gtkmenushell.h>
#include <gtk/gtkmenutoolbutton.h>
#include <gtk/gtkmessagedialog.h>
#include <gtk/gtkmodelbutton.h>
#include <gtk/gtkmodules.h>
#include <gtk/gtkmountoperation.h>
#include <gtk/gtknotebook.h>
@@ -152,6 +153,7 @@
#include <gtk/gtkpaned.h>
#include <gtk/gtkplacessidebar.h>
#include <gtk/gtkpopover.h>
#include <gtk/gtkpopovermenu.h>
#include <gtk/gtkprintcontext.h>
#include <gtk/gtkprintoperation.h>
#include <gtk/gtkprintoperationpreview.h>

View File

@@ -23,6 +23,8 @@
#include "gtkwidget.h"
#include "gtkwidgetprivate.h"
#include "gtkdebug.h"
#include "gtkmodelbutton.h"
#include "gtktypebuiltins.h"
#include <string.h>
@@ -66,6 +68,8 @@ struct _GtkActionHelper
gboolean enabled;
gboolean active;
GtkButtonRole role;
gint reporting;
};
@@ -74,6 +78,7 @@ enum
PROP_0,
PROP_ENABLED,
PROP_ACTIVE,
PROP_ROLE,
N_PROPS
};
@@ -107,6 +112,17 @@ gtk_action_helper_report_change (GtkActionHelper *helper,
}
break;
case PROP_ROLE:
{
GParamSpec *pspec;
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (helper->widget), "role");
if (pspec && G_PARAM_SPEC_VALUE_TYPE (pspec) == GTK_TYPE_BUTTON_ROLE)
g_object_set (G_OBJECT (helper->widget), "role", helper->role, NULL);
}
break;
default:
g_assert_not_reached ();
}
@@ -146,10 +162,19 @@ gtk_action_helper_action_added (GtkActionHelper *helper,
GTK_NOTE(ACTIONS, g_message("actionhelper: %s found and enabled", helper->action_name));
if (helper->target != NULL && state != NULL)
helper->active = g_variant_equal (state, helper->target);
{
helper->active = g_variant_equal (state, helper->target);
helper->role = GTK_BUTTON_ROLE_RADIO;
}
else if (state != NULL && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
helper->active = g_variant_get_boolean (state);
{
helper->active = g_variant_get_boolean (state);
helper->role = GTK_BUTTON_ROLE_CHECK;
}
else
{
helper->role = GTK_BUTTON_ROLE_NORMAL;
}
if (should_emit_signals)
{
@@ -158,6 +183,8 @@ gtk_action_helper_action_added (GtkActionHelper *helper,
if (helper->active)
gtk_action_helper_report_change (helper, PROP_ACTIVE);
gtk_action_helper_report_change (helper, PROP_ROLE);
}
}
@@ -242,6 +269,10 @@ gtk_action_helper_get_property (GObject *object, guint prop_id,
g_value_set_boolean (value, helper->active);
break;
case PROP_ROLE:
g_value_set_enum (value, helper->role);
break;
default:
g_assert_not_reached ();
}
@@ -313,6 +344,10 @@ gtk_action_helper_class_init (GtkActionHelperClass *class)
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
gtk_action_helper_pspecs[PROP_ACTIVE] = g_param_spec_boolean ("active", "active", "active", FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
gtk_action_helper_pspecs[PROP_ROLE] = g_param_spec_enum ("role", "role", "role",
GTK_TYPE_BUTTON_ROLE,
GTK_BUTTON_ROLE_NORMAL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (class, N_PROPS, gtk_action_helper_pspecs);
}

View File

@@ -51,6 +51,7 @@
#include "gtkwidgetpath.h"
#include "a11y/gtkcontaineraccessible.h"
#include "a11y/gtkcontaineraccessibleprivate.h"
#include "gtkpopovermenu.h"
/**
* SECTION:gtkcontainer
@@ -608,7 +609,10 @@ gtk_container_buildable_set_child_property (GtkContainer *container,
GValue gvalue = G_VALUE_INIT;
GError *error = NULL;
if (gtk_widget_get_parent (child) != (GtkWidget *)container && !GTK_IS_ASSISTANT (container) && !GTK_IS_ACTION_BAR (container))
if (gtk_widget_get_parent (child) != (GtkWidget *)container &&
!GTK_IS_ASSISTANT (container) &&
!GTK_IS_ACTION_BAR (container) &&
!GTK_IS_POPOVER_MENU (container))
{
/* This can happen with internal children of complex widgets.
* Silently ignore the child properties in this case. We explicitly

View File

@@ -50,13 +50,14 @@ struct _GtkMenuSectionBox
G_DEFINE_TYPE (GtkMenuSectionBox, gtk_menu_section_box, GTK_TYPE_BOX)
void gtk_menu_section_box_sync_separators (GtkMenuSectionBox *box,
gint *n_items);
void gtk_menu_section_box_new_submenu (GtkMenuTrackerItem *item,
GtkMenuSectionBox *toplevel,
GtkWidget *focus);
GtkWidget * gtk_menu_section_box_new_section (GtkMenuTrackerItem *item,
GtkMenuSectionBox *parent);
static void gtk_menu_section_box_sync_separators (GtkMenuSectionBox *box,
gint *n_items);
static void gtk_menu_section_box_new_submenu (GtkMenuTrackerItem *item,
GtkMenuSectionBox *toplevel,
GtkWidget *focus,
const gchar *name);
static GtkWidget * gtk_menu_section_box_new_section (GtkMenuTrackerItem *item,
GtkMenuSectionBox *parent);
static void
gtk_menu_section_box_sync_item (GtkWidget *widget,
@@ -79,7 +80,7 @@ gtk_menu_section_box_sync_item (GtkWidget *widget,
* no items before it
* (rule 5: these rules don't apply exactly the same way for subsections)
*/
void
static void
gtk_menu_section_box_sync_separators (GtkMenuSectionBox *box,
gint *n_items)
{
@@ -209,16 +210,12 @@ close_submenu (GtkWidget *button,
gpointer data)
{
GtkMenuTrackerItem *item = data;
GtkWidget *stack;
GtkWidget *parent;
GtkWidget *focus;
if (gtk_menu_tracker_item_get_should_request_show (item))
gtk_menu_tracker_item_request_submenu_shown (item, FALSE);
focus = GTK_WIDGET (g_object_get_data (G_OBJECT (button), "focus"));
get_ancestors (focus, GTK_TYPE_STACK, &stack, &parent);
gtk_stack_set_visible_child (GTK_STACK (stack), parent);
gtk_widget_grab_focus (focus);
}
@@ -227,16 +224,12 @@ open_submenu (GtkWidget *button,
gpointer data)
{
GtkMenuTrackerItem *item = data;
GtkWidget *stack;
GtkWidget *child;
GtkWidget *focus;
if (gtk_menu_tracker_item_get_should_request_show (item))
gtk_menu_tracker_item_request_submenu_shown (item, TRUE);
focus = GTK_WIDGET (g_object_get_data (G_OBJECT (button), "focus"));
get_ancestors (focus, GTK_TYPE_STACK, &stack, &child);
gtk_stack_set_visible_child (GTK_STACK (stack), child);
gtk_widget_grab_focus (focus);
}
@@ -254,17 +247,25 @@ gtk_menu_section_box_insert_func (GtkMenuTrackerItem *item,
}
else if (gtk_menu_tracker_item_get_has_link (item, G_MENU_LINK_SUBMENU))
{
widget = g_object_new (GTK_TYPE_MODEL_BUTTON, "has-submenu", TRUE, NULL);
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),
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);
gtk_menu_section_box_new_submenu (item, box->toplevel, widget);
gtk_widget_show (widget);
get_ancestors (GTK_WIDGET (box->item_box), GTK_TYPE_STACK, &stack, &parent);
gtk_container_child_get (GTK_CONTAINER (stack), parent, "name", &name, NULL);
gtk_menu_section_box_new_submenu (item, box->toplevel, widget, name);
g_free (name);
}
else
{
widget = gtk_model_button_new ();
g_object_bind_property (item, "label", widget, "text", G_BINDING_SYNC_CREATE);
if (box->iconic)
@@ -276,10 +277,8 @@ gtk_menu_section_box_insert_func (GtkMenuTrackerItem *item,
g_object_bind_property (item, "icon", widget, "icon", G_BINDING_SYNC_CREATE);
g_object_bind_property (item, "sensitive", widget, "sensitive", G_BINDING_SYNC_CREATE);
g_object_bind_property (item, "role", widget, "action-role", G_BINDING_SYNC_CREATE);
g_object_bind_property (item, "toggled", widget, "toggled", G_BINDING_SYNC_CREATE);
g_object_bind_property (item, "accel", widget, "accel", G_BINDING_SYNC_CREATE);
g_object_bind_property (item, "role", widget, "role", G_BINDING_SYNC_CREATE);
g_object_bind_property (item, "toggled", widget, "active", G_BINDING_SYNC_CREATE);
g_signal_connect (widget, "clicked", G_CALLBACK (gtk_popover_item_activate), item);
}
@@ -367,10 +366,11 @@ gtk_menu_section_box_new_toplevel (GtkStack *stack,
gtk_widget_show (GTK_WIDGET (box));
}
void
static void
gtk_menu_section_box_new_submenu (GtkMenuTrackerItem *item,
GtkMenuSectionBox *toplevel,
GtkWidget *focus)
GtkWidget *focus,
const gchar *name)
{
GtkMenuSectionBox *box;
GtkWidget *button;
@@ -378,10 +378,11 @@ gtk_menu_section_box_new_submenu (GtkMenuTrackerItem *item,
box = g_object_new (GTK_TYPE_MENU_SECTION_BOX, "margin", 10, NULL);
button = g_object_new (GTK_TYPE_MODEL_BUTTON,
"has-submenu", TRUE,
"menu-name", name,
"inverted", TRUE,
"centered", TRUE,
NULL);
g_object_bind_property (item, "label", button, "text", G_BINDING_SYNC_CREATE);
g_object_bind_property (item, "icon", button, "icon", G_BINDING_SYNC_CREATE);
@@ -404,7 +405,7 @@ gtk_menu_section_box_new_submenu (GtkMenuTrackerItem *item,
box);
}
GtkWidget *
static GtkWidget *
gtk_menu_section_box_new_section (GtkMenuTrackerItem *item,
GtkMenuSectionBox *parent)
{

View File

@@ -31,19 +31,53 @@
#include "gtkrender.h"
#include "gtkstylecontext.h"
#include "gtktypebuiltins.h"
#include "gtkstack.h"
#include "gtkpopover.h"
#include "gtkintl.h"
/**
* SECTION:gtkmodelbutton
* @Short_description: A button that uses a GAction as model
* @Title: GtkModelButton
*
* GtkModelButton is a button class that can use a #GAction as its model.
*
* Model buttons are used when popovers from a menu model with
* gtk_popover_new_from_model(); they can also be used manually in
* a #GtkPopoverMenu.
*
* When the action is specified via the #GtkActionable:action-name
* and #GtkActionable:action-target properties, the role of the button
* (i.e. whether it is a plain, check or radio button) is determined by
* the type of the action and doesn't have to be explicitly specified
* with the #GtkModelButton:role property.
*
* The content of the button is specified by the #GtkModelButton:text
* and #GtkModelButton:icon properties.
*
* The appearance of model buttons can be influenced with the
* #GtkModelButton:centered and #GtkModelButton:iconic properties.
*
* Model buttons have built-in support for submenus in #GtkPopoverMenu.
* To make a GtkModelButton that opens a submenu when activated, set
* the #GtkModelButton:menu-name property. To make a button that goes
* back to the parent menu, you should set the #GtkModelButton:inverted
* property to place the submenu indicator at the opposite side.
*/
struct _GtkModelButton
{
GtkButton parent_instance;
GtkWidget *box;
GtkWidget *image;
GtkWidget *label;
gboolean toggled;
gboolean has_submenu;
gboolean active;
gboolean centered;
gboolean inverted;
gboolean iconic;
GtkMenuTrackerItemRole role;
gchar *menu_name;
GtkButtonRole role;
};
typedef GtkButtonClass GtkModelButtonClass;
@@ -53,20 +87,41 @@ G_DEFINE_TYPE (GtkModelButton, gtk_model_button, GTK_TYPE_BUTTON)
enum
{
PROP_0,
PROP_ACTION_ROLE,
PROP_ROLE,
PROP_ICON,
PROP_TEXT,
PROP_TOGGLED,
PROP_ACCEL,
PROP_HAS_SUBMENU,
PROP_ACTIVE,
PROP_MENU_NAME,
PROP_INVERTED,
PROP_CENTERED,
PROP_ICONIC
PROP_ICONIC,
LAST_PROPERTY
};
static GParamSpec *properties[LAST_PROPERTY] = { NULL, };
static void
gtk_model_button_set_action_role (GtkModelButton *button,
GtkMenuTrackerItemRole role)
gtk_model_button_update_state (GtkModelButton *button)
{
GtkStateFlags state;
if (button->role == GTK_BUTTON_ROLE_NORMAL)
return;
state = gtk_widget_get_state_flags (GTK_WIDGET (button));
state &= ~GTK_STATE_FLAG_CHECKED;
if (button->active && !button->menu_name)
state |= GTK_STATE_FLAG_CHECKED;
gtk_widget_set_state_flags (GTK_WIDGET (button), state, TRUE);
}
static void
gtk_model_button_set_role (GtkModelButton *button,
GtkButtonRole role)
{
AtkObject *accessible;
AtkRole a11y_role;
@@ -75,20 +130,19 @@ gtk_model_button_set_action_role (GtkModelButton *button,
return;
button->role = role;
gtk_widget_queue_draw (GTK_WIDGET (button));
accessible = gtk_widget_get_accessible (GTK_WIDGET (button));
switch (role)
{
case GTK_MENU_TRACKER_ITEM_ROLE_NORMAL:
case GTK_BUTTON_ROLE_NORMAL:
a11y_role = ATK_ROLE_PUSH_BUTTON;
break;
case GTK_MENU_TRACKER_ITEM_ROLE_CHECK:
case GTK_BUTTON_ROLE_CHECK:
a11y_role = ATK_ROLE_CHECK_BOX;
break;
case GTK_MENU_TRACKER_ITEM_ROLE_RADIO:
case GTK_BUTTON_ROLE_RADIO:
a11y_role = ATK_ROLE_RADIO_BUTTON;
break;
@@ -97,6 +151,10 @@ gtk_model_button_set_action_role (GtkModelButton *button,
}
atk_object_set_role (accessible, a11y_role);
gtk_model_button_update_state (button);
gtk_widget_queue_draw (GTK_WIDGET (button));
g_object_notify_by_pspec (G_OBJECT (button), properties[PROP_ROLE]);
}
static void
@@ -118,6 +176,7 @@ gtk_model_button_set_icon (GtkModelButton *button,
{
gtk_image_set_from_gicon (GTK_IMAGE (button->image), icon, GTK_ICON_SIZE_MENU);
update_visibility (button);
g_object_notify_by_pspec (G_OBJECT (button), properties[PROP_ICON]);
}
static void
@@ -126,64 +185,56 @@ gtk_model_button_set_text (GtkModelButton *button,
{
gtk_label_set_text_with_mnemonic (GTK_LABEL (button->label), text);
update_visibility (button);
g_object_notify_by_pspec (G_OBJECT (button), properties[PROP_TEXT]);
}
static void
gtk_model_button_set_accel (GtkModelButton *button,
const gchar *accel)
gtk_model_button_set_active (GtkModelButton *button,
gboolean active)
{
/* ignore */
}
if (button->active == active)
return;
static void
gtk_model_button_update_state (GtkModelButton *button)
{
GtkStateFlags state;
state = gtk_widget_get_state_flags (GTK_WIDGET (button));
state &= ~GTK_STATE_FLAG_CHECKED;
if (button->toggled && !button->has_submenu)
state |= GTK_STATE_FLAG_CHECKED;
gtk_widget_set_state_flags (GTK_WIDGET (button), state, TRUE);
}
static void
gtk_model_button_set_toggled (GtkModelButton *button,
gboolean toggled)
{
button->toggled = toggled;
button->active = active;
gtk_model_button_update_state (button);
gtk_widget_queue_draw (GTK_WIDGET (button));
g_object_notify_by_pspec (G_OBJECT (button), properties[PROP_ACTIVE]);
}
static void
gtk_model_button_set_has_submenu (GtkModelButton *button,
gboolean has_submenu)
gtk_model_button_set_menu_name (GtkModelButton *button,
const gchar *menu_name)
{
button->has_submenu = has_submenu;
g_free (button->menu_name);
button->menu_name = g_strdup (menu_name);
gtk_model_button_update_state (button);
gtk_widget_queue_resize (GTK_WIDGET (button));
g_object_notify_by_pspec (G_OBJECT (button), properties[PROP_MENU_NAME]);
}
static void
gtk_model_button_set_inverted (GtkModelButton *button,
gboolean inverted)
{
if (button->inverted == inverted)
return;
button->inverted = inverted;
gtk_widget_queue_resize (GTK_WIDGET (button));
g_object_notify_by_pspec (G_OBJECT (button), properties[PROP_INVERTED]);
}
static void
gtk_model_button_set_centered (GtkModelButton *button,
gboolean centered)
{
if (button->centered == centered)
return;
button->centered = centered;
gtk_widget_set_halign (button->box, button->centered ? GTK_ALIGN_CENTER : GTK_ALIGN_FILL);
gtk_widget_queue_draw (GTK_WIDGET (button));
g_object_notify_by_pspec (G_OBJECT (button), properties[PROP_CENTERED]);
}
static void
@@ -192,6 +243,9 @@ gtk_model_button_set_iconic (GtkModelButton *button,
{
GtkStyleContext *context;
if (button->iconic == iconic)
return;
button->iconic = iconic;
context = gtk_widget_get_style_context (GTK_WIDGET (button));
@@ -210,6 +264,58 @@ gtk_model_button_set_iconic (GtkModelButton *button,
update_visibility (button);
gtk_widget_queue_resize (GTK_WIDGET (button));
g_object_notify_by_pspec (G_OBJECT (button), properties[PROP_ICONIC]);
}
static void
gtk_model_button_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkModelButton *button = GTK_MODEL_BUTTON (object);
switch (prop_id)
{
case PROP_ROLE:
g_value_set_enum (value, button->role);
break;
case PROP_ICON:
{
GIcon *icon;
gtk_image_get_gicon (GTK_IMAGE (button->image), &icon, NULL);
g_value_set_object (value, icon);
}
break;
case PROP_TEXT:
g_value_set_string (value, gtk_label_get_text (GTK_LABEL (button->label)));
break;
case PROP_ACTIVE:
g_value_set_boolean (value, button->active);
break;
case PROP_MENU_NAME:
g_value_set_string (value, button->menu_name);
break;
case PROP_INVERTED:
g_value_set_boolean (value, button->inverted);
break;
case PROP_CENTERED:
g_value_set_boolean (value, button->centered);
break;
case PROP_ICONIC:
g_value_set_boolean (value, button->iconic);
break;
default:
g_assert_not_reached ();
}
}
static void
@@ -222,8 +328,8 @@ gtk_model_button_set_property (GObject *object,
switch (prop_id)
{
case PROP_ACTION_ROLE:
gtk_model_button_set_action_role (button, g_value_get_enum (value));
case PROP_ROLE:
gtk_model_button_set_role (button, g_value_get_enum (value));
break;
case PROP_ICON:
@@ -234,16 +340,12 @@ gtk_model_button_set_property (GObject *object,
gtk_model_button_set_text (button, g_value_get_string (value));
break;
case PROP_TOGGLED:
gtk_model_button_set_toggled (button, g_value_get_boolean (value));
case PROP_ACTIVE:
gtk_model_button_set_active (button, g_value_get_boolean (value));
break;
case PROP_ACCEL:
gtk_model_button_set_accel (button, g_value_get_string (value));
break;
case PROP_HAS_SUBMENU:
gtk_model_button_set_has_submenu (button, g_value_get_boolean (value));
case PROP_MENU_NAME:
gtk_model_button_set_menu_name (button, g_value_get_string (value));
break;
case PROP_INVERTED:
@@ -311,7 +413,7 @@ has_sibling_with_indicator (GtkWidget *button)
continue;
if (!sibling->centered &&
(sibling->has_submenu || sibling->role != GTK_MENU_TRACKER_ITEM_ROLE_NORMAL))
(sibling->menu_name || sibling->role != GTK_BUTTON_ROLE_NORMAL))
{
has_indicator = TRUE;
break;
@@ -326,7 +428,7 @@ has_sibling_with_indicator (GtkWidget *button)
static gboolean
needs_indicator (GtkModelButton *button)
{
if (button->role != GTK_MENU_TRACKER_ITEM_ROLE_NORMAL)
if (button->role != GTK_BUTTON_ROLE_NORMAL)
return TRUE;
return has_sibling_with_indicator (GTK_WIDGET (button));
@@ -388,12 +490,12 @@ gtk_model_button_get_preferred_width (GtkWidget *widget,
}
static void
gtk_model_button_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
gint width,
gint *minimum,
gint *natural,
gint *minimum_baseline,
gint *natural_baseline)
gtk_model_button_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
gint width,
gint *minimum,
gint *natural,
gint *minimum_baseline,
gint *natural_baseline)
{
GtkModelButton *button = GTK_MODEL_BUTTON (widget);
GtkWidget *child;
@@ -570,9 +672,7 @@ gtk_model_button_draw (GtkWidget *widget,
gint baseline;
if (model_button->iconic)
{
return GTK_WIDGET_CLASS (gtk_model_button_parent_class)->draw (widget, cr);
}
return GTK_WIDGET_CLASS (gtk_model_button_parent_class)->draw (widget, cr);
context = gtk_widget_get_style_context (widget);
width = gtk_widget_get_allocated_width (widget);
@@ -603,7 +703,7 @@ gtk_model_button_draw (GtkWidget *widget,
width - 2 * border_width,
height - 2 * border_width);
if (model_button->has_submenu)
if (model_button->menu_name)
{
GtkStateFlags state;
@@ -620,14 +720,14 @@ gtk_model_button_draw (GtkWidget *widget,
gtk_render_expander (context, cr, x, y, indicator_size, indicator_size);
gtk_style_context_restore (context);
}
else if (model_button->role == GTK_MENU_TRACKER_ITEM_ROLE_CHECK)
else if (model_button->role == GTK_BUTTON_ROLE_CHECK)
{
gtk_style_context_save (context);
gtk_style_context_add_class (context, GTK_STYLE_CLASS_CHECK);
gtk_render_check (context, cr, x, y, indicator_size, indicator_size);
gtk_style_context_restore (context);
}
else if (model_button->role == GTK_MENU_TRACKER_ITEM_ROLE_RADIO)
else if (model_button->role == GTK_BUTTON_ROLE_RADIO)
{
gtk_style_context_save (context);
gtk_style_context_add_class (context, GTK_STYLE_CLASS_RADIO);
@@ -655,12 +755,36 @@ gtk_model_button_draw (GtkWidget *widget,
return FALSE;
}
static void
gtk_model_button_clicked (GtkButton *button)
{
GtkModelButton *model_button = GTK_MODEL_BUTTON (button);
if (model_button->menu_name != NULL)
{
GtkWidget *stack;
stack = gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_STACK);
if (stack != NULL)
gtk_stack_set_visible_child_name (GTK_STACK (stack), model_button->menu_name);
}
else if (model_button->role == GTK_BUTTON_ROLE_NORMAL)
{
GtkWidget *popover;
popover = gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_POPOVER);
if (popover != NULL)
gtk_widget_hide (popover);
}
}
static void
gtk_model_button_class_init (GtkModelButtonClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
GtkButtonClass *button_class = GTK_BUTTON_CLASS (class);
object_class->get_property = gtk_model_button_get_property;
object_class->set_property = gtk_model_button_set_property;
widget_class->get_preferred_width = gtk_model_button_get_preferred_width;
@@ -671,36 +795,131 @@ gtk_model_button_class_init (GtkModelButtonClass *class)
widget_class->size_allocate = gtk_model_button_size_allocate;
widget_class->draw = gtk_model_button_draw;
g_object_class_install_property (object_class, PROP_ACTION_ROLE,
g_param_spec_enum ("action-role", "", "",
GTK_TYPE_MENU_TRACKER_ITEM_ROLE,
GTK_MENU_TRACKER_ITEM_ROLE_NORMAL,
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_ICON,
g_param_spec_object ("icon", "", "", G_TYPE_ICON,
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_TEXT,
g_param_spec_string ("text", "", "", NULL,
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_TOGGLED,
g_param_spec_boolean ("toggled", "", "", FALSE,
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_ACCEL,
g_param_spec_string ("accel", "", "", NULL,
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_HAS_SUBMENU,
g_param_spec_boolean ("has-submenu", "", "", FALSE,
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_INVERTED,
g_param_spec_boolean ("inverted", "", "", FALSE,
button_class->clicked = gtk_model_button_clicked;
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_CENTERED,
g_param_spec_boolean ("centered", "", "", FALSE,
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_ICONIC,
g_param_spec_boolean ("iconic", "", "", TRUE,
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
/**
* GtkModelButton:role:
*
* Specifies whether the button is a plain, check or radio button.
* When #GtkActionable:action-name is set, the role will be determined
* from the action and does not have to be set explicitly.
*
* Since: 3.16
*/
properties[PROP_ROLE] =
g_param_spec_enum ("role",
P_("Role"),
P_("The role of this button"),
GTK_TYPE_BUTTON_ROLE,
GTK_BUTTON_ROLE_NORMAL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkModelButton:icon:
*
* A #GIcon that will be used if iconic appearance for the button is
* desired.
*
* Since: 3.16
*/
properties[PROP_ICON] =
g_param_spec_object ("icon",
P_("Icon"),
P_("The icon"),
G_TYPE_ICON,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkModelButton:text:
*
* The label for the button.
*
* Since: 3.16
*/
properties[PROP_TEXT] =
g_param_spec_string ("text",
P_("Text"),
P_("The text"),
NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkModelButton:active:
*
* The state of the button. This is reflecting the state of the associated
* #GAction.
*
* Since: 3.16
*/
properties[PROP_ACTIVE] =
g_param_spec_boolean ("active",
P_("Active"),
P_("Active"),
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* 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.
*
* Since: 3.16
*/
properties[PROP_MENU_NAME] =
g_param_spec_string ("menu-name",
P_("Menu name"),
P_("The name of the menu to open"),
NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkModelButton:inverted:
*
* Whether to show the submenu indicator at the opposite side than normal.
* This property should be set for model buttons that 'go back' to a parent
* menu.
*
* Since: 3.16
*/
properties[PROP_INVERTED] =
g_param_spec_boolean ("inverted",
P_("Inverted"),
P_("Whether the menu is a parent"),
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkModelButton:centered:
*
* Wether to render the button contents centered instead of left-aligned.
* This property should be set for title-like items.
*
* Since: 3.16
*/
properties[PROP_CENTERED] =
g_param_spec_boolean ("centered",
P_("Centered"),
P_("Whether to center the contents"),
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkModelButton:iconic:
*
* If this property is set, the button will show an icon if one is set.
* If no icon is set, the text will be used. This is typically used for
* horizontal sections of linked buttons.
*
* Since: 3.16
*/
properties[PROP_ICONIC] =
g_param_spec_boolean ("iconic",
P_("Iconic"),
P_("Whether to prefer the icon over text"),
TRUE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, LAST_PROPERTY, properties);
gtk_widget_class_set_accessible_role (GTK_WIDGET_CLASS (class), ATK_ROLE_PUSH_BUTTON);
}
@@ -710,12 +929,10 @@ gtk_model_button_init (GtkModelButton *button)
{
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
button->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
g_object_set (button->box,
"margin-start", 12,
"margin-end", 12,
"margin-top", 3,
"margin-bottom", 3,
NULL);
gtk_widget_set_margin_start (button->box, 12);
gtk_widget_set_margin_end (button->box, 12);
gtk_widget_set_margin_top (button->box, 3);
gtk_widget_set_margin_bottom (button->box, 3);
gtk_widget_set_halign (button->box, GTK_ALIGN_FILL);
gtk_widget_show (button->box);
button->image = gtk_image_new ();

View File

@@ -20,21 +20,41 @@
#ifndef __GTK_MODEL_BUTTON_H__
#define __GTK_MODEL_BUTTON_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkwidget.h>
G_BEGIN_DECLS
#define GTK_TYPE_MODEL_BUTTON (gtk_model_button_get_type ())
#define GTK_MODEL_BUTTON(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
GTK_TYPE_MODEL_BUTTON, GtkModelButton))
#define GTK_IS_MODEL_BUTTON(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
GTK_TYPE_MODEL_BUTTON))
#define GTK_TYPE_MODEL_BUTTON (gtk_model_button_get_type ())
#define GTK_MODEL_BUTTON(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
GTK_TYPE_MODEL_BUTTON, GtkModelButton))
#define GTK_IS_MODEL_BUTTON(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
GTK_TYPE_MODEL_BUTTON))
typedef struct _GtkModelButton GtkModelButton;
typedef struct _GtkModelButton GtkModelButton;
GType gtk_model_button_get_type (void) G_GNUC_CONST;
/**
* GtkButtonRole:
* @GTK_BUTTON_ROLE_NORMAL: A plain button
* @GTK_BUTTON_ROLE_CHECK: A check button
* @GTK_BUTTON_ROLE_RADIO: A radio button
*
* The role specifies the desired appearance of a #GtkModelButton.
*/
typedef enum {
GTK_BUTTON_ROLE_NORMAL,
GTK_BUTTON_ROLE_CHECK,
GTK_BUTTON_ROLE_RADIO
} GtkButtonRole;
GtkWidget * gtk_model_button_new (void);
GDK_AVAILABLE_IN_3_16
GType gtk_model_button_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_3_16
GtkWidget * gtk_model_button_new (void);
G_END_DECLS

259
gtk/gtkpopovermenu.c Normal file
View File

@@ -0,0 +1,259 @@
/* GTK - The GIMP Toolkit
* Copyright © 2014 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkpopovermenu.h"
#include "gtkstack.h"
#include "gtkbox.h"
#include "gtkintl.h"
/**
* SECTION:gtkpopovermenu
* @Short_description: Popovers to use as menus
*
* GtkPopoverMenu is a subclass of #GtkPopover that treats its
* childen like menus and allows switching between them. It is
* meant to be used primarily together with #GtkModelButton, but
* any widget can be used, such as #GtkSpinButton or #GtkScale.
*
* To add a child as a submenu, set the #GtkPopoverMenu:submenu
* child property to the name of the submenu. To let the user open
* this submenu, add a #GtkModelButton whose #GtkModelButton:menu-name
* property is set to the name you've given to the submenu.
*
* By convention, the first child of a submenu should be a #GtkModelButton
* to switch back to the parent menu. Such a button should use the
* #GtkModelButton:inverted and #GtkModelButton:centered properties
* to achieve a title-like appearance and place the submenu indicator
* at the opposite side. To switch back to the main menu, use "main"
* as the menu name.
*/
struct _GtkPopoverMenu
{
GtkPopover parent_instance;
};
enum {
CHILD_PROP_SUBMENU = 1
};
G_DEFINE_TYPE (GtkPopoverMenu, gtk_popover_menu, GTK_TYPE_POPOVER)
static void
gtk_popover_menu_init (GtkPopoverMenu *popover)
{
GtkWidget *stack;
stack = gtk_stack_new ();
gtk_stack_set_vhomogeneous (GTK_STACK (stack), FALSE);
gtk_stack_set_transition_type (GTK_STACK (stack), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT);
gtk_widget_show (stack);
gtk_container_add (GTK_CONTAINER (popover), stack);
}
static void
gtk_popover_menu_map (GtkWidget *widget)
{
GTK_WIDGET_CLASS (gtk_popover_menu_parent_class)->map (widget);
gtk_popover_menu_open_submenu (GTK_POPOVER_MENU (widget), "main");
}
static void
gtk_popover_menu_unmap (GtkWidget *widget)
{
gtk_popover_menu_open_submenu (GTK_POPOVER_MENU (widget), "main");
GTK_WIDGET_CLASS (gtk_popover_menu_parent_class)->unmap (widget);
}
static void
gtk_popover_menu_add (GtkContainer *container,
GtkWidget *child)
{
GtkWidget *stack;
stack = gtk_bin_get_child (GTK_BIN (container));
if (stack == NULL)
{
gtk_widget_set_parent (child, GTK_WIDGET (container));
_gtk_bin_set_child (GTK_BIN (container), child);
}
else
{
gchar *name;
if (gtk_stack_get_child_by_name (GTK_STACK (stack), "main"))
name = "submenu";
else
name = "main";
gtk_stack_add_named (GTK_STACK (stack), child, name);
}
}
static void
gtk_popover_menu_remove (GtkContainer *container,
GtkWidget *child)
{
GtkWidget *stack;
stack = gtk_bin_get_child (GTK_BIN (container));
if (child == stack)
GTK_CONTAINER_CLASS (gtk_popover_menu_parent_class)->remove (container, child);
else
gtk_container_remove (GTK_CONTAINER (stack), child);
}
static void
gtk_popover_menu_get_child_property (GtkContainer *container,
GtkWidget *child,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkWidget *stack;
stack = gtk_bin_get_child (GTK_BIN (container));
if (child == stack)
return;
switch (property_id)
{
case CHILD_PROP_SUBMENU:
{
gchar *name;
gtk_container_child_get (GTK_CONTAINER (stack), child, "name", &name, NULL);
g_value_set_string (value, name);
}
break;
default:
GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
break;
}
}
static void
gtk_popover_menu_set_child_property (GtkContainer *container,
GtkWidget *child,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkWidget *stack;
stack = gtk_bin_get_child (GTK_BIN (container));
if (child == stack)
return;
switch (property_id)
{
case CHILD_PROP_SUBMENU:
{
const gchar *name;
name = g_value_get_string (value);
gtk_container_child_set (GTK_CONTAINER (stack), child, "name", name, NULL);
}
break;
default:
GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
break;
}
}
static void
gtk_popover_menu_class_init (GtkPopoverMenuClass *klass)
{
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
widget_class->map = gtk_popover_menu_map;
widget_class->unmap = gtk_popover_menu_unmap;
container_class->add = gtk_popover_menu_add;
container_class->remove = gtk_popover_menu_remove;
container_class->set_child_property = gtk_popover_menu_set_child_property;
container_class->get_child_property = gtk_popover_menu_get_child_property;
/**
* GtkPopoverMenu:submenu:
*
* The submenu child property specifies the name of the submenu
* If it is %NULL or "main", the child is used as the main menu,
* which is shown initially when the popover is mapped.
*
* Since: 3.16
*/
gtk_container_class_install_child_property (container_class,
CHILD_PROP_SUBMENU,
g_param_spec_string ("submenu",
P_("Submenu"),
P_("The name of the submenu"),
NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
/**
* gtk_popover_menu_new:
*
* Creates a new popover menu.
*
* Returns: a new #GtkPopoverMenu
*
* Since: 3.16
*/
GtkWidget *
gtk_popover_menu_new (void)
{
return g_object_new (GTK_TYPE_POPOVER_MENU, NULL);
}
/**
* gtk_popover_menu_open_submenu:
* @popover: a #GtkPopoverMenu
* @name: the name of the menu to switch to
*
* Opens a submenu of the @popover. The @name
* must be one of the names given to the submenus
* of @popover with #GtkPopoverMenu:submenu, or
* "main" to switch back to the main menu.
*
* #GtkModelButton will open submenus automatically
* when the #GtkModelButton:menu-name property is set,
* so this function is only needed when you are using
* other kinds of widgets to initiate menu changes.
*
* Since: 3.16
*/
void
gtk_popover_menu_open_submenu (GtkPopoverMenu *popover,
const gchar *name)
{
GtkWidget *stack;
g_return_if_fail (GTK_IS_POPOVER_MENU (popover));
stack = gtk_bin_get_child (GTK_BIN (popover));
gtk_stack_set_visible_child_name (GTK_STACK (stack), name);
}

62
gtk/gtkpopovermenu.h Normal file
View File

@@ -0,0 +1,62 @@
/* GTK - The GIMP Toolkit
* Copyright © 2014 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GTK_POPOVER_MENU_H__
#define __GTK_POPOVER_MENU_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkpopover.h>
G_BEGIN_DECLS
#define GTK_TYPE_POPOVER_MENU (gtk_popover_menu_get_type ())
#define GTK_POPOVER_MENU(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_POPOVER_MENU, GtkPopoverMenu))
#define GTK_POPOVER_MENU_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GTK_TYPE_POPOVER_MENU, GtkPopoverMenuClass))
#define GTK_IS_POPOVER_MENU(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_POPOVER_MENU))
#define GTK_IS_POPOVER_MENU_CLASS(o) (G_TYPE_CHECK_CLASS_TYPE ((o), GTK_TYPE_POPOVER_MENU))
#define GTK_POPOVER_MENU_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_POPOVER_MENU, GtkPopoverMenuClass))
typedef struct _GtkPopoverMenu GtkPopoverMenu;
typedef struct _GtkPopoverMenuClass GtkPopoverMenuClass;
struct _GtkPopoverMenuClass
{
GtkPopoverClass parent_class;
/*< private >*/
/* Padding for future expansion */
gpointer reserved[10];
};
GDK_AVAILABLE_IN_3_16
GType gtk_popover_menu_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_3_16
GtkWidget * gtk_popover_menu_new (void);
GDK_AVAILABLE_IN_3_16
void gtk_popover_menu_open_submenu (GtkPopoverMenu *popover,
const gchar *name);
G_END_DECLS
#endif /* __GTK_POPOVER_MENU_H__ */

View File

@@ -21,6 +21,7 @@ LDADD = \
$(GDK_DEP_LIBS) \
-lm
if USE_X11
testsocket_programs = testsocket testsocket_child
endif

569
tests/popover2.ui Normal file
View File

@@ -0,0 +1,569 @@
<?xml version="1.0"?>
<interface>
<object class="GtkPopoverMenu" id="popover">
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="margin">10</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">6</property>
<property name="margin-bottom">3</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="halign">start</property>
<property name="label">Edit</property>
<style>
<class name="separator"/>
</style>
</object>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="orientation">horizontal</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.cut</property>
<property name="text">Cut</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.copy</property>
<property name="text">Copy</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.paste</property>
<property name="text">Paste</property>
</object>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="orientation">horizontal</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">3</property>
<property name="margin-bottom">3</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">action1</property>
<property name="text">No action</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action2</property>
<property name="text">Toggle</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action2a</property>
<property name="text">Another Toggle</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">6</property>
<property name="margin-bottom">3</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="halign">start</property>
<property name="label">Middle Section</property>
<style>
<class name="separator"/>
</style>
</object>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="orientation">horizontal</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action3</property>
<property name="action-target">'three'</property>
<property name="text">Radio 1</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action3</property>
<property name="action-target">'four'</property>
<property name="text">Radio 2</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text">Submenu 1</property>
<property name="menu-name">submenu1</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text">Submenu 2</property>
<property name="menu-name">submenu2</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">6</property>
<property name="margin-bottom">3</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="halign">start</property>
<property name="label">End Section</property>
<style>
<class name="separator"/>
</style>
</object>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="orientation">horizontal</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action9</property>
<property name="text">Another Item 9</property>
<property name="icon">icon9</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action10</property>
<property name="text">Another Item 10</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="margin">10</property>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text">Submenu 1</property>
<property name="inverted">True</property>
<property name="centered">True</property>
<property name="menu-name">main</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">6</property>
<property name="margin-bottom">3</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="halign">start</property>
<property name="label">5555</property>
<style>
<class name="separator"/>
</style>
</object>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="orientation">horizontal</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action5</property>
<property name="text">Item 5</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action5</property>
<property name="text">Item 5a</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action5</property>
<property name="text">Item 5b</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">horizontal</property>
<property name="halign">fill</property>
<property name="margin-top">10</property>
<style>
<class name="linked"/>
</style>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text">List</property>
<property name="action-name">top.set-view</property>
<property name="action-target">'list'</property>
<property name="iconic">True</property>
<property name="centered">True</property>
<property name="icon">icon-list</property>
</object>
<packing>
<property name="expand">True</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text">Grid</property>
<property name="action-name">top.set-view</property>
<property name="action-target">'grid'</property>
<property name="iconic">True</property>
<property name="centered">True</property>
<property name="icon">icon-grid</property>
</object>
<packing>
<property name="expand">True</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action5</property>
<property name="text">Item 5c</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action5</property>
<property name="text">Item 5d</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">6</property>
<property name="margin-bottom">3</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="halign">start</property>
<property name="label">Format</property>
<style>
<class name="separator"/>
</style>
</object>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="orientation">horizontal</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">horizontal</property>
<property name="halign">fill</property>
<property name="margin-top">10</property>
<style>
<class name="linked"/>
</style>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text">Bold</property>
<property name="action-name">top.bold</property>
<property name="iconic">True</property>
<property name="centered">True</property>
</object>
<packing>
<property name="expand">True</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text">Italic</property>
<property name="action-name">top.italic</property>
<property name="iconic">True</property>
<property name="centered">True</property>
<property name="icon">icon-italic</property>
</object>
<packing>
<property name="expand">True</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text">Strikethrough</property>
<property name="action-name">top.strikethrough</property>
<property name="iconic">True</property>
<property name="centered">True</property>
<property name="icon">icon-strikethrough</property>
</object>
<packing>
<property name="expand">True</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text">Underline</property>
<property name="action-name">top.underline</property>
<property name="iconic">True</property>
<property name="centered">True</property>
<property name="icon">icon-underline</property>
</object>
<packing>
<property name="expand">True</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">6</property>
<property name="margin-bottom">3</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="halign">start</property>
<property name="label">6666</property>
<style>
<class name="separator"/>
</style>
</object>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="orientation">horizontal</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action6</property>
<property name="text">Item 6</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action6</property>
<property name="text">Item 6a</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action6</property>
<property name="text">Item 6b</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action6</property>
<property name="text">Item 6c</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action6</property>
<property name="text">Item 6d</property>
</object>
</child>
</object>
<packing>
<property name="submenu">submenu1</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="margin">10</property>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text">Submenu 2</property>
<property name="inverted">True</property>
<property name="centered">True</property>
<property name="menu-name">main</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action7</property>
<property name="text">Item 7</property>
</object>
</child>
<child>
<object class="GtkScale">
<property name="visible">True</property>
<property name="draw-value">False</property>
<property name="adjustment">adjustment</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text">Subsubmenu</property>
<property name="icon">icon9</property>
<property name="menu-name">subsubmenu</property>
</object>
</child>
</object>
<packing>
<property name="submenu">submenu2</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="margin">10</property>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text">Subsubmenu</property>
<property name="inverted">True</property>
<property name="centered">True</property>
<property name="menu-name">submenu2</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">action8</property>
<property name="text">Item 8</property>
</object>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="orientation">horizontal</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">3</property>
<property name="margin-bottom">3</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action9</property>
<property name="text">Item 9</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="action-name">top.action10</property>
<property name="text">Item 10</property>
</object>
</child>
</object>
<packing>
<property name="submenu">subsubmenu</property>
</packing>
</child>
</object>
<object class="GThemedIcon" id="icon9">
<property name="name">preferences-desktop-font</property>
</object>
<object class="GThemedIcon" id="icon-grid">
<property name="name">view-grid-symbolic</property>
</object>
<object class="GThemedIcon" id="icon-list">
<property name="name">view-list-symbolic</property>
</object>
<object class="GThemedIcon" id="icon-italic">
<property name="name">format-text-italic-symbolic</property>
</object>
<object class="GThemedIcon" id="icon-strikethrough">
<property name="name">format-text-strikethrough-symbolic</property>
</object>
<object class="GThemedIcon" id="icon-underline">
<property name="name">format-text-underline-symbolic</property>
</object>
<object class="GtkAdjustment" id="adjustment">
<property name="lower">0</property>
<property name="upper">100</property>
<property name="value">50</property>
</object>
</interface>

View File

@@ -30,16 +30,20 @@ static GActionEntry entries[] = {
{ "action10", activate, NULL, NULL, NULL }
};
int main (int argc, char *argv[])
int
main (int argc, char *argv[])
{
GtkWidget *win;
GtkWidget *box;
GtkWidget *button;
GtkWidget *button2;
GtkBuilder *builder;
GMenuModel *model;
GSimpleActionGroup *actions;
GtkWidget *overlay;
GtkWidget *grid;
GtkWidget *popover;
GtkWidget *popover2;
GtkWidget *label;
GtkWidget *check;
GtkWidget *combo;
@@ -76,30 +80,36 @@ int main (int argc, char *argv[])
builder = gtk_builder_new_from_file ("popover.ui");
model = (GMenuModel *)gtk_builder_get_object (builder, "menu");
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
button = gtk_menu_button_new ();
gtk_container_add (GTK_CONTAINER (box), button);
button2 = gtk_menu_button_new ();
gtk_container_add (GTK_CONTAINER (box), button2);
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (button), model);
gtk_menu_button_set_use_popover (GTK_MENU_BUTTON (button), TRUE);
popover = GTK_WIDGET (gtk_menu_button_get_popover (GTK_MENU_BUTTON (button)));
g_object_set (button, "margin", 10, NULL);
gtk_widget_set_halign (button, GTK_ALIGN_END);
gtk_widget_set_valign (button, GTK_ALIGN_START);
gtk_overlay_add_overlay (GTK_OVERLAY (overlay), button);
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);
g_object_set (box, "margin", 10, NULL);
gtk_widget_set_halign (box, GTK_ALIGN_END);
gtk_widget_set_valign (box, GTK_ALIGN_START);
gtk_overlay_add_overlay (GTK_OVERLAY (overlay), box);
label = gtk_label_new ("Popover hexpand");
check = gtk_check_button_new ();
g_object_bind_property (popover, "hexpand",
check, "active",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
g_object_bind_property (check, "active", popover, "hexpand", G_BINDING_DEFAULT);
g_object_bind_property (check, "active", popover2, "hexpand", G_BINDING_DEFAULT);
gtk_grid_attach (GTK_GRID (grid), label , 1, 1, 1, 1);
gtk_grid_attach (GTK_GRID (grid), check, 2, 1, 1, 1);
label = gtk_label_new ("Popover vexpand");
check = gtk_check_button_new ();
g_object_bind_property (popover, "vexpand",
check, "active",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
g_object_bind_property (check, "active", popover, "vexpand", G_BINDING_DEFAULT);
g_object_bind_property (check, "active", popover2, "vexpand", G_BINDING_DEFAULT);
gtk_grid_attach (GTK_GRID (grid), label , 1, 2, 1, 1);
gtk_grid_attach (GTK_GRID (grid), check, 2, 2, 1, 1);
@@ -110,9 +120,8 @@ int main (int argc, char *argv[])
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "left", "Left");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "right", "Right");
g_object_bind_property (button, "direction",
combo, "active",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
g_object_bind_property (combo, "active", button, "direction", G_BINDING_DEFAULT);
g_object_bind_property (combo, "active", button2, "direction", G_BINDING_DEFAULT);
gtk_grid_attach (GTK_GRID (grid), label , 1, 3, 1, 1);
gtk_grid_attach (GTK_GRID (grid), combo, 2, 3, 1, 1);
@@ -122,9 +131,7 @@ int main (int argc, char *argv[])
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "start", "Start");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "end", "End");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "center", "Center");
g_object_bind_property (button, "halign",
combo, "active",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
g_object_bind_property (combo, "active", box, "halign", G_BINDING_DEFAULT);
gtk_grid_attach (GTK_GRID (grid), label , 1, 4, 1, 1);
gtk_grid_attach (GTK_GRID (grid), combo, 2, 4, 1, 1);
@@ -134,9 +141,7 @@ int main (int argc, char *argv[])
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "start", "Start");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "end", "End");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "center", "Center");
g_object_bind_property (button, "valign",
combo, "active",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
g_object_bind_property (combo, "active", box, "valign", G_BINDING_DEFAULT);
gtk_grid_attach (GTK_GRID (grid), label , 1, 5, 1, 1);
gtk_grid_attach (GTK_GRID (grid), combo, 2, 5, 1, 1);