Test handling of empty menus.
2003-09-30 Matthias Clasen <maclas@gmx.de> * tests/merge-*.ui: * tests/testmerge.c: Test handling of empty menus. * gtk/gtkuimanager.c (_gtk_menu_is_empty): New function to determine whether a menu is empty. Used in gtkaction.c. (update_smart_separators): Also update the visibility of empty menus. (update_node): When creating a new menu proxy, insert an "Empty" menu item which only gets shown if the menu is empty. * gtk/gtkaction.c (gtk_action_class_init): Document the meaning of "is_important" for menu proxies. (_gtk_action_sync_menu_visible): New function to sync the visibility of menu proxies. Used in gtkuimanager.c. (gtk_action_sync_visible): New function to sync the visibility of proxies.
This commit is contained in:
committed by
Matthias Clasen
parent
039c6b3bb1
commit
57f13d815d
18
ChangeLog
18
ChangeLog
@@ -1,3 +1,21 @@
|
||||
2003-09-30 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
* tests/merge-*.ui:
|
||||
* tests/testmerge.c: Test handling of empty menus.
|
||||
|
||||
* gtk/gtkuimanager.c (_gtk_menu_is_empty): New function to determine
|
||||
whether a menu is empty. Used in gtkaction.c.
|
||||
(update_smart_separators): Also update the visibility of empty menus.
|
||||
(update_node): When creating a new menu proxy, insert an "Empty" menu
|
||||
item which only gets shown if the menu is empty.
|
||||
|
||||
* gtk/gtkaction.c (gtk_action_class_init): Document the meaning of
|
||||
"is_important" for menu proxies.
|
||||
(_gtk_action_sync_menu_visible): New function to sync the visibility
|
||||
of menu proxies. Used in gtkuimanager.c.
|
||||
(gtk_action_sync_visible): New function to sync the visibility of
|
||||
proxies.
|
||||
|
||||
Tue Sep 30 21:43:34 2003 Kristian Rietveld <kris@gtk.org>
|
||||
|
||||
* gtk/gtkcombobox.c (gtk_combo_box_menu_button_press): set
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
2003-09-30 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
* tests/merge-*.ui:
|
||||
* tests/testmerge.c: Test handling of empty menus.
|
||||
|
||||
* gtk/gtkuimanager.c (_gtk_menu_is_empty): New function to determine
|
||||
whether a menu is empty. Used in gtkaction.c.
|
||||
(update_smart_separators): Also update the visibility of empty menus.
|
||||
(update_node): When creating a new menu proxy, insert an "Empty" menu
|
||||
item which only gets shown if the menu is empty.
|
||||
|
||||
* gtk/gtkaction.c (gtk_action_class_init): Document the meaning of
|
||||
"is_important" for menu proxies.
|
||||
(_gtk_action_sync_menu_visible): New function to sync the visibility
|
||||
of menu proxies. Used in gtkuimanager.c.
|
||||
(gtk_action_sync_visible): New function to sync the visibility of
|
||||
proxies.
|
||||
|
||||
Tue Sep 30 21:43:34 2003 Kristian Rietveld <kris@gtk.org>
|
||||
|
||||
* gtk/gtkcombobox.c (gtk_combo_box_menu_button_press): set
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
2003-09-30 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
* tests/merge-*.ui:
|
||||
* tests/testmerge.c: Test handling of empty menus.
|
||||
|
||||
* gtk/gtkuimanager.c (_gtk_menu_is_empty): New function to determine
|
||||
whether a menu is empty. Used in gtkaction.c.
|
||||
(update_smart_separators): Also update the visibility of empty menus.
|
||||
(update_node): When creating a new menu proxy, insert an "Empty" menu
|
||||
item which only gets shown if the menu is empty.
|
||||
|
||||
* gtk/gtkaction.c (gtk_action_class_init): Document the meaning of
|
||||
"is_important" for menu proxies.
|
||||
(_gtk_action_sync_menu_visible): New function to sync the visibility
|
||||
of menu proxies. Used in gtkuimanager.c.
|
||||
(gtk_action_sync_visible): New function to sync the visibility of
|
||||
proxies.
|
||||
|
||||
Tue Sep 30 21:43:34 2003 Kristian Rietveld <kris@gtk.org>
|
||||
|
||||
* gtk/gtkcombobox.c (gtk_combo_box_menu_button_press): set
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
2003-09-30 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
* tests/merge-*.ui:
|
||||
* tests/testmerge.c: Test handling of empty menus.
|
||||
|
||||
* gtk/gtkuimanager.c (_gtk_menu_is_empty): New function to determine
|
||||
whether a menu is empty. Used in gtkaction.c.
|
||||
(update_smart_separators): Also update the visibility of empty menus.
|
||||
(update_node): When creating a new menu proxy, insert an "Empty" menu
|
||||
item which only gets shown if the menu is empty.
|
||||
|
||||
* gtk/gtkaction.c (gtk_action_class_init): Document the meaning of
|
||||
"is_important" for menu proxies.
|
||||
(_gtk_action_sync_menu_visible): New function to sync the visibility
|
||||
of menu proxies. Used in gtkuimanager.c.
|
||||
(gtk_action_sync_visible): New function to sync the visibility of
|
||||
proxies.
|
||||
|
||||
Tue Sep 30 21:43:34 2003 Kristian Rietveld <kris@gtk.org>
|
||||
|
||||
* gtk/gtkcombobox.c (gtk_combo_box_menu_button_press): set
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
2003-09-30 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
* tests/merge-*.ui:
|
||||
* tests/testmerge.c: Test handling of empty menus.
|
||||
|
||||
* gtk/gtkuimanager.c (_gtk_menu_is_empty): New function to determine
|
||||
whether a menu is empty. Used in gtkaction.c.
|
||||
(update_smart_separators): Also update the visibility of empty menus.
|
||||
(update_node): When creating a new menu proxy, insert an "Empty" menu
|
||||
item which only gets shown if the menu is empty.
|
||||
|
||||
* gtk/gtkaction.c (gtk_action_class_init): Document the meaning of
|
||||
"is_important" for menu proxies.
|
||||
(_gtk_action_sync_menu_visible): New function to sync the visibility
|
||||
of menu proxies. Used in gtkuimanager.c.
|
||||
(gtk_action_sync_visible): New function to sync the visibility of
|
||||
proxies.
|
||||
|
||||
Tue Sep 30 21:43:34 2003 Kristian Rietveld <kris@gtk.org>
|
||||
|
||||
* gtk/gtkcombobox.c (gtk_combo_box_menu_button_press): set
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
2003-09-30 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
* gtk/tmpl/gtkuimanager.sgml: Add a section about empty menus.
|
||||
|
||||
* gdk/tmpl/keys.sgml: Small addition.
|
||||
|
||||
* gdk/gdk-sections.txt: Add GdkDisplayClass and GdkScreenClass.
|
||||
|
||||
@@ -146,15 +146,31 @@ actions even if they have no visible proxies.
|
||||
<refsect2 id="Smart-Separators">
|
||||
<title>Smart Separators</title>
|
||||
<para>
|
||||
The separators created by #GtkUIManager are "smart", i.e. they do not show up in the
|
||||
UI unless they end up between two visible menu or tool items. Separators which are located
|
||||
at the very beginning or end of the menu or toolbar containing them, or multiple separators
|
||||
next to each other, are hidden. This is a useful feature, since the merging of UI elements
|
||||
from multiple sources can make it hard or impossible to determine in advance whether a
|
||||
separator will end up in such an unfortunate position.
|
||||
The separators created by #GtkUIManager are "smart", i.e. they do not show up
|
||||
in the UI unless they end up between two visible menu or tool items. Separators
|
||||
which are located at the very beginning or end of the menu or toolbar
|
||||
containing them, or multiple separators next to each other, are hidden. This
|
||||
is a useful feature, since the merging of UI elements from multiple sources
|
||||
can make it hard or impossible to determine in advance whether a separator
|
||||
will end up in such an unfortunate position.
|
||||
</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Empty Menus</title>
|
||||
<para>
|
||||
Submenus pose similar problems to separators inconnection with merging. It is
|
||||
impossible to know in advance whether they will end up empty after merging.
|
||||
#GtkUIManager offers two ways to treat empty submenus:
|
||||
<itemizedlist>
|
||||
<listitem><para>make them disappear by hiding the menu item they're attached to
|
||||
</para></listitem>
|
||||
<listitem><para>add an insensitive "Empty" item
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
The behaviour is chosen based on the "is_important" property of the action
|
||||
to which the submenu is associated.
|
||||
</para>
|
||||
</refsect2>
|
||||
|
||||
<!-- ##### SECTION See_Also ##### -->
|
||||
<para>
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "gtkmarshalers.h"
|
||||
#include "gtkmenuitem.h"
|
||||
#include "gtkstock.h"
|
||||
#include "gtktearoffmenuitem.h"
|
||||
#include "gtktoolbutton.h"
|
||||
#include "gtktoolbar.h"
|
||||
|
||||
@@ -88,7 +89,7 @@ enum
|
||||
PROP_STOCK_ID,
|
||||
PROP_IS_IMPORTANT,
|
||||
PROP_SENSITIVE,
|
||||
PROP_VISIBLE,
|
||||
PROP_VISIBLE
|
||||
};
|
||||
|
||||
static void gtk_action_init (GtkAction *action);
|
||||
@@ -216,7 +217,7 @@ gtk_action_class_init (GtkActionClass *klass)
|
||||
PROP_IS_IMPORTANT,
|
||||
g_param_spec_boolean ("is_important",
|
||||
_("Is important"),
|
||||
_("Whether the action is considered important. When TRUE, toolitem proxies for this action show text in GTK_TOOLBAR_BOTH_HORIZ mode"),
|
||||
_("Whether the action is considered important. When TRUE, toolitem proxies for this action show text in GTK_TOOLBAR_BOTH_HORIZ mode, and empty menu proxies for this action are not hidden."),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE));
|
||||
g_object_class_install_property (gobject_class,
|
||||
@@ -234,7 +235,6 @@ gtk_action_class_init (GtkActionClass *klass)
|
||||
TRUE,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
|
||||
/**
|
||||
* GtkAction::activate:
|
||||
* @action: the #GtkAction
|
||||
@@ -530,6 +530,63 @@ gtk_action_sync_property (GtkAction *action,
|
||||
g_value_unset (&value);
|
||||
}
|
||||
|
||||
/**
|
||||
* _gtk_action_sync_menu_visible:
|
||||
* @action: a #GtkAction, or %NULL to determine the action from @proxy
|
||||
* @proxy: a proxy menu item
|
||||
* @empty: whether the submenu attached to @proxy is empty
|
||||
*
|
||||
* Updates the visibility of @proxy from the visibility of @action
|
||||
* according to the following rules:
|
||||
* <itemizedlist>
|
||||
* <listitem><para>if @action is invisible, @proxy is too
|
||||
* </para></listitem>
|
||||
* <listitem><para>if @empty is %TRUE, hide @proxy unless @action is important
|
||||
* </para></listitem>
|
||||
* </itemizedlist>
|
||||
*
|
||||
* This function is used in the implementation of #GtkUIManager.
|
||||
**/
|
||||
void
|
||||
_gtk_action_sync_menu_visible (GtkAction *action,
|
||||
GtkWidget *proxy,
|
||||
gboolean empty)
|
||||
{
|
||||
gboolean visible, important;
|
||||
|
||||
g_return_if_fail (GTK_IS_MENU_ITEM (proxy));
|
||||
g_return_if_fail (action == NULL || GTK_IS_ACTION (action));
|
||||
|
||||
if (action == NULL)
|
||||
action = g_object_get_data (G_OBJECT (proxy), "gtk-action");
|
||||
|
||||
g_object_get (G_OBJECT (action),
|
||||
"visible", &visible,
|
||||
"is_important", &important,
|
||||
NULL);
|
||||
|
||||
g_object_set (G_OBJECT (proxy),
|
||||
"visible", visible && (important || !empty),
|
||||
NULL);
|
||||
}
|
||||
|
||||
gboolean _gtk_menu_is_empty (GtkWidget *menu);
|
||||
|
||||
static void
|
||||
gtk_action_sync_visible (GtkAction *action,
|
||||
GParamSpec *pspec,
|
||||
GtkWidget *proxy)
|
||||
{
|
||||
if (GTK_IS_MENU_ITEM (proxy))
|
||||
{
|
||||
GtkWidget *menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy));
|
||||
|
||||
_gtk_action_sync_menu_visible (action, proxy, _gtk_menu_is_empty (menu));
|
||||
}
|
||||
else
|
||||
gtk_action_sync_property (action, pspec, proxy);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_action_sync_label (GtkAction *action,
|
||||
GParamSpec *pspec,
|
||||
@@ -627,7 +684,7 @@ connect_proxy (GtkAction *action,
|
||||
gtk_widget_set_sensitive (proxy, action->private_data->sensitive);
|
||||
|
||||
g_signal_connect_object (action, "notify::visible",
|
||||
G_CALLBACK (gtk_action_sync_property), proxy, 0);
|
||||
G_CALLBACK (gtk_action_sync_visible), proxy, 0);
|
||||
if (action->private_data->visible)
|
||||
gtk_widget_show (proxy);
|
||||
else
|
||||
|
||||
@@ -60,8 +60,7 @@ typedef enum
|
||||
NODE_TYPE_ACCELERATOR
|
||||
} NodeType;
|
||||
|
||||
|
||||
typedef struct _Node Node;
|
||||
typedef struct _Node Node;
|
||||
|
||||
struct _Node {
|
||||
NodeType type;
|
||||
@@ -71,7 +70,7 @@ struct _Node {
|
||||
GQuark action_name;
|
||||
GtkAction *action;
|
||||
GtkWidget *proxy;
|
||||
GtkWidget *extra; /*GtkMenu for submenus, second separator for placeholders*/
|
||||
GtkWidget *extra; /* second separator for placeholders */
|
||||
|
||||
GList *uifiles;
|
||||
|
||||
@@ -831,13 +830,14 @@ start_element_handler (GMarkupParseContext *context,
|
||||
const gchar *action;
|
||||
GQuark action_quark;
|
||||
gboolean top;
|
||||
|
||||
|
||||
gboolean raise_error = TRUE;
|
||||
|
||||
node_name = NULL;
|
||||
action = NULL;
|
||||
action_quark = 0;
|
||||
top = FALSE;
|
||||
|
||||
for (i = 0; attribute_names[i] != NULL; i++)
|
||||
{
|
||||
if (!strcmp (attribute_names[i], "name"))
|
||||
@@ -1642,12 +1642,55 @@ find_toolbar_position (GNode *node,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* _gtk_menu_is_empty:
|
||||
* @menu: a #GtkMenu or %NULL
|
||||
*
|
||||
* Determines whether @menu is empty. A menu is considered empty if it
|
||||
* the only visible children are tearoff menu items or "filler" menu
|
||||
* items which were inserted to mark the menu as empty.
|
||||
*
|
||||
* This function is used by #GtkAction.
|
||||
*
|
||||
* Return value: whether @menu is empty.
|
||||
**/
|
||||
gboolean
|
||||
_gtk_menu_is_empty (GtkWidget *menu)
|
||||
{
|
||||
GList *children, *cur;
|
||||
|
||||
g_return_val_if_fail (menu == NULL || GTK_IS_MENU (menu), TRUE);
|
||||
|
||||
if (!menu)
|
||||
return TRUE;
|
||||
|
||||
children = gtk_container_get_children (GTK_CONTAINER (menu));
|
||||
|
||||
cur = children;
|
||||
while (cur)
|
||||
{
|
||||
if (GTK_WIDGET_VISIBLE (cur->data))
|
||||
{
|
||||
if (!GTK_IS_TEAROFF_MENU_ITEM (cur->data) &&
|
||||
!g_object_get_data (cur->data, "gtk-empty-menu-item"))
|
||||
return FALSE;
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
enum {
|
||||
SEPARATOR_MODE_SMART,
|
||||
SEPARATOR_MODE_VISIBLE,
|
||||
SEPARATOR_MODE_HIDDEN
|
||||
};
|
||||
|
||||
void _gtk_action_sync_menu_visible (GtkAction *action,
|
||||
GtkWidget *proxy,
|
||||
gboolean empty);
|
||||
|
||||
static void
|
||||
update_smart_separators (GtkWidget *proxy)
|
||||
{
|
||||
@@ -1661,15 +1704,23 @@ update_smart_separators (GtkWidget *proxy)
|
||||
if (parent)
|
||||
{
|
||||
gboolean visible;
|
||||
gboolean empty;
|
||||
GList *children, *cur, *last;
|
||||
GtkWidget *filler;
|
||||
|
||||
children = gtk_container_get_children (GTK_CONTAINER (parent));
|
||||
|
||||
visible = FALSE;
|
||||
last = NULL;
|
||||
empty = TRUE;
|
||||
filler = NULL;
|
||||
|
||||
cur = children;
|
||||
while (cur)
|
||||
{
|
||||
if (g_object_get_data (cur->data, "gtk-empty-menu-item"))
|
||||
filler = cur->data;
|
||||
|
||||
if (GTK_IS_SEPARATOR_MENU_ITEM (cur->data) ||
|
||||
GTK_IS_SEPARATOR_TOOL_ITEM (cur->data))
|
||||
{
|
||||
@@ -1701,10 +1752,13 @@ update_smart_separators (GtkWidget *proxy)
|
||||
else if (GTK_WIDGET_VISIBLE (cur->data))
|
||||
{
|
||||
last = NULL;
|
||||
if (GTK_IS_TEAROFF_MENU_ITEM (cur->data))
|
||||
if (GTK_IS_TEAROFF_MENU_ITEM (cur->data) || cur->data == filler)
|
||||
visible = FALSE;
|
||||
else
|
||||
visible = TRUE;
|
||||
{
|
||||
visible = TRUE;
|
||||
empty = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
cur = cur->next;
|
||||
@@ -1712,6 +1766,15 @@ update_smart_separators (GtkWidget *proxy)
|
||||
|
||||
if (last)
|
||||
gtk_widget_hide (GTK_WIDGET (last->data));
|
||||
|
||||
if (GTK_IS_MENU (parent))
|
||||
{
|
||||
GtkWidget *item;
|
||||
|
||||
item = gtk_menu_get_attach_widget (GTK_MENU (parent));
|
||||
_gtk_action_sync_menu_visible (NULL, item, empty);
|
||||
g_object_set (G_OBJECT (filler), "visible", empty, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1843,12 +1906,20 @@ update_node (GtkUIManager *self,
|
||||
if (find_menu_position (node, &menushell, &pos))
|
||||
{
|
||||
GtkWidget *tearoff;
|
||||
GtkWidget *filler;
|
||||
|
||||
info->proxy = gtk_action_create_menu_item (action);
|
||||
menu = gtk_menu_new ();
|
||||
tearoff = gtk_tearoff_menu_item_new ();
|
||||
gtk_widget_set_no_show_all (tearoff, TRUE);
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
|
||||
filler = gtk_menu_item_new_with_label (_("Empty"));
|
||||
g_object_set_data (G_OBJECT (filler),
|
||||
"gtk-empty-menu-item",
|
||||
GINT_TO_POINTER (TRUE));
|
||||
gtk_widget_set_sensitive (filler, FALSE);
|
||||
gtk_widget_set_no_show_all (filler, TRUE);
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (menu), filler);
|
||||
gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), menu);
|
||||
gtk_menu_shell_insert (GTK_MENU_SHELL (menushell), info->proxy, pos);
|
||||
}
|
||||
|
||||
@@ -3,15 +3,20 @@
|
||||
<menubar>
|
||||
<menu name="FileMenu" action="FileMenuAction">
|
||||
<menuitem name="Open" action="OpenAction" />
|
||||
<menuitem name="Bold" action="BoldAction" />
|
||||
</menu>
|
||||
<menu name="EditMenu" action="EditMenuAction">
|
||||
<menuitem name="Cut" action="CutAction" />
|
||||
<menu name="EmptyMenu1" action="EmptyMenu1Action">
|
||||
</menu>
|
||||
<menu name="EmptyMenu2" action="EmptyMenu2Action">
|
||||
</menu>
|
||||
</menu>
|
||||
<placeholder name="TestPlaceholder" />
|
||||
</menubar>
|
||||
<toolbar name="toolbar1">
|
||||
<placeholder name="ToolbarPlaceholder">
|
||||
<toolitem name="nb2" action="NewAction" />
|
||||
<toolitem name="nb2" action="New2Action" />
|
||||
<separator name="Sep1" />
|
||||
</placeholder>
|
||||
<toolitem name="NewButton" action="NewAction" />
|
||||
|
||||
@@ -8,6 +8,14 @@
|
||||
<menuitem name="Quit" action="QuitAction" />
|
||||
<separator name="Sep3" />
|
||||
</menu>
|
||||
<menu name="EditMenu" action="EditMenuAction">
|
||||
<menu name="EmptyMenu1" action="EmptyMenu1Action">
|
||||
<menuitem name="Cut" action="CutAction" />
|
||||
</menu>
|
||||
<menu name="EmptyMenu2" action="EmptyMenu2Action">
|
||||
<menuitem name="Cut" action="CutAction" />
|
||||
</menu>
|
||||
</menu>
|
||||
<menu name="HelpMenu" action="HelpMenuAction">
|
||||
<menuitem name="About" action="AboutAction" />
|
||||
</menu>
|
||||
|
||||
@@ -123,6 +123,8 @@ static GtkActionEntry entries[] = {
|
||||
{ "EditMenuAction", NULL, "_Edit" },
|
||||
{ "HelpMenuAction", NULL, "_Help" },
|
||||
{ "JustifyMenuAction", NULL, "_Justify" },
|
||||
{ "EmptyMenu1Action", NULL, "Empty 1" },
|
||||
{ "EmptyMenu2Action", NULL, "Empty 2" },
|
||||
{ "Test", NULL, "Test" },
|
||||
|
||||
{ "QuitAction", GTK_STOCK_QUIT, NULL, "<control>q", "Quit", G_CALLBACK (gtk_main_quit) },
|
||||
@@ -541,6 +543,10 @@ main (int argc, char **argv)
|
||||
gtk_action_group_add_actions (action_group,
|
||||
entries, n_entries,
|
||||
NULL);
|
||||
action = gtk_action_group_get_action (action_group, "EmptyMenu1Action");
|
||||
g_object_set (G_OBJECT (action), "is_important", TRUE, NULL);
|
||||
action = gtk_action_group_get_action (action_group, "EmptyMenu2Action");
|
||||
g_object_set (G_OBJECT (action), "is_important", FALSE, NULL);
|
||||
gtk_action_group_add_toggle_actions (action_group,
|
||||
toggle_entries, n_toggle_entries,
|
||||
NULL);
|
||||
|
||||
Reference in New Issue
Block a user