NAME
Gtk2::Ex::MenuView -- menu of items from a TreeModel
SYNOPSIS
use Gtk2::Ex::MenuView;
my $menuview = Gtk2::Ex::MenuView->new (model => $my_model);
$menuview->signal_connect (item_create_or_update => \&my_item_create);
$menuview->signal_connect (activate => \&my_item_activate);
sub my_item_create {
my ($menuview, $item, $model, $path, $iter) = @_;
# make item if not already done
if (! $item) {
$item = Gtk2::MenuItem->new_with_label ('');
}
# update its settings to display model data
my $label = $item->get_child;
my $str = $model->get ($iter, 0); # column 0
$label->set_text ($str);
return $item; # created or updated item
}
sub my_item_activate {
my ($menuview, $item, $model, $path, $iter) = @_;
print "an item was activated ...\n";
}
WIDGET HIERARCHY
Gtk2::Ex::MenuView
is a subclass of Gtk2::Menu
,
Gtk2::Widget
Gtk2::Container
Gtk2::MenuShell
Gtk2::Menu
Gtk2::Ex::MenuView::Menu
Gtk2::Ex::MenuView
The MenuView::Menu
subclass is an implementation detail (things common to the toplevel menu and submenus), so don't rely on that.
DESCRIPTION
Gtk2::Ex::MenuView
presents rows and sub-rows of a Gtk2::TreeModel
as a menu and sub-menus. The items update with changes in the model.
+--------------+
| Item One |
| Item Two => | +------------+
| Item Three | | Sub-item A |
+--------------+ | Sub-item B |
+------------+
The menu items are created by an item-create-or-update
callback signal described below. It offers flexibility for item class and settings, but there's no default, so you must connect a handler or nothing is displayed. The code shown in the SYNOPSIS above is typical, creating an item if not already created, then updating the item display settings to show the row contents.
FUNCTIONS
-
Create and return a new
Gtk2::Ex::MenuView
object. Optional key/value pairs set initial properties as perGlib::Object->new
.
Item Access
-
Return the
Gtk2::MenuItem
child at the given path or coordinates. If it doesn't exist yet thenitem-create-or-update
is called to create it.The return is
undef
if the requested path doesn't exist in the model, or path is empty, or ifitem-create-or-update
returnsundef
for no item for that row.item_at_indices
is handy on alist-only
model to get an item just by number (0 for the first row), without going through aGtk2::TreePath
object. For example,$item = $menuview->item_at_indices (0); # first menu item
Item Location
The following can methods are good in an item signal handler for locating an item within its MenuView.
$path = Gtk2::Ex::MenuView->item_get_path ($item)
-
Return a
Gtk2::TreePath
which is the location of$item
in its MenuView. Returnundef
if$item
has been removed from the menuview (eg. if its row was deleted).The
$path
object is newly created and can be modified or kept by the caller. -
Return a combination menuview, model, path and iter which is the item's MenuView and location. Return no values if
$item
has been removed from the menuview.$path
and$iter
are both newly created and can be modified or kept by the caller.$model
is the same as$menuview->get('model')
, but returned since it's often wanted for fetching data (using$iter
).
PROPERTIES
model
(object implementingGtk2::TreeModel
, default undef)-
The TreeModel to display. Until this is set the menu is empty.
The menu is updated to the new model data by
item-create-or-update
calls as necessary. Any popped-up submenus which don't exist in the new model are popped-down, but those existing in both the old and new model remain up. want-visible
(Gtk2::Ex::MenuView::WantVisible
enum, default 'show_all')-
Whether to automatically make items visible. The possible values are
no don't touch items' visible property show use $item->show when first created show_all use $item->show_all when first created
The default
show_all
makes each item and all its child widgets visible. This is usually what you want to see it on the screen.show
orno
allow items or some of their child parts to be invisible at times. See "Item Visibility" below for further notes.(The enum value is
show_all
with an underscore. It corresponds to the method name and C function name, though it's unlike other enum nicks which use hyphens.) want-activate
(Gtk2::Ex::MenuView::WantActivate
enum, default 'leaf')-
Whether to emit the MenuView
activate
signal (described below) for item activation. The possible values areno don't emit leaf emit for leaf items all emit for all items
The default
leaf
will suit most applications.all
emits on non-leaf nodes too, such as when clicking to pop up a submenu, which isn't really an item selection and not usually of interest.Setting
no
doesn't emit theactivate
signal. This saves some signal connections and lookups and can be used if you want different connections on different items, or perhaps only care about a few item activations, or have a MenuItem subclass with its own activate handler in the class.Currently this setting only affects newly created items, not existing ones.
SIGNALS
-
Emitted as a callback to the application asking it for a menu item for the given model row.
$item
is the previously returned item for this row, orundef
if none yet. The return should be aGtk2::MenuItem
or subclass. It can be either newly created or simply the existing$item
with updated settings. If no item is wanted at all for the row then returnundef
.$menuview->signal_connect (item_create_or_update => \&my_item_handler); sub my_item_handler { my ($menuview, $item, $model, $path, $iter, $userdata) = @_; if (! $item) { $item = ...; # create something } # ... apply item settings to display row data return $item; }
MenuView owns any item returned to it by
item-create-or-update
and will$item->destroy
when no longer wanted. (destroy
lets items break any circular references and in particular is necessary for an item with aGtk2::AccelLabel
child, per notes in Gtk2::MenuItem.)The order
item-create-or-update
calls are made for rows is unspecified. They're also done on a "lazy" basis, so items are only created or updated when the menu is visible, or its size is requested, etc.An
item-create-or-update
handler can call$menuview->item_at_path
etc to get another row item. This will do a recursiveitem-create-or-update
if the item isn't already up-to-date. Of course the item currently updating or any higher one in progress cannot be obtained.An
item-create-or-update
must not insert, delete or reorder the model rows. -
Emitted when a menu item is activated, either by the user clicking it, or a programmatic
$item->activate
(including an$item->set_active
on CheckMenuItems).sub my_activate { my ($menuview, $item, $model, $path, $iter, $userdata) = @_; print "Item activated ", $path->to_string, "\n"; }
The parameters happen to be the same as
item-create-or-update
above, except of course$item
is notundef
.If you change row contents then bear in mind it might cause an
item-create-or-update
call updating$item
. If that callback decides to return a brand new item then you'll be left with only the old one (now destroyed).You can connect directly to the individual item
activate
signals (with asignal_connect
initem-create-or-update
). The unified MenuViewactivate
is designed for the common case where most items do something similar based on the model data.An
activate
handler must not insert, delete or reorder rows in the model, since doing so may invalidate the$path
and$iter
. Those objects are passed to each connected handler without tracking row changes by the handlers. This restriction doesn't apply to anactivate
handler on an individual item, as it doesn't have path/iter parameters, and as long as the itemactivate
won't be emitted by code within anitem-create-or-update
(likeset_active
on a CheckMenuItem does) and which thus has path and iters in use.
DETAILS
Item Create and Update
The way item-create-or-update
combines the create and update operations makes it easy to sometimes update an existing item or sometimes create a new one, probably sharing the code that applies display settings.
An update can return a new item if a different class is wanted for different row data, or if some settings on an item can be made only when constructing it, not updated later. Otherwise an update is usually just a matter of fetching row data and putting it in properties in the item or child.
A slack item-create-or-update
can create a new item every time. If model rows don't change often then this is perfectly respectable and may save a line or two of code. For example,
sub my_item_create_or_update_handler {
my ($menuview, $item, $model, $path, $iter, $userdata) = @_;
return Gtk2::MenuItem->new_with_label ($model->get($iter,0));
}
Item Visibility
Usually all items should be made visible and MenuView does that automatically by default. If you want to manage visibility yourself then set want-visiblity
to no
to make MenuView leave it alone completely, or show
to have MenuView just $item->show
the item itself, not recursing into its children.
An invisible item or a return of undef
from item-create-or-update
both result in nothing displayed. If items are slow to create you might keep them in the menu but invisible when unwanted (trading memory against slowness of creation). Visibility could be controlled from something external too.
From a user interface point of view it's often better to make items insensitive (greyed out) when not applicable etc. You can set the item sensitive
property (see Gtk2::Widget) from item-create-or-update
according to row data, or link it up to something external, etc.
Check Items
One use for a Gtk2::CheckMenuItem
is to have the active
property display and control a column in the model. In item-create-or-update
do $item->set_active
to make the item show the model data, then in the activate
signal handler do $model->set
to put the item's new active
state into the model. See examples/checkitem.pl for a complete sample program.
$model->set
under activate
will cause MenuView to call item-create-or-update
again because the model row has changed, and $item->set_active
there may emit the activate
signal again. This would be an endless recursion except that set_activate
notices when the item is already in the state requested and does nothing. Be sure to have a cutoff like that.
Another possibility is to tie the check item active
property to something external using signal handlers to keep them in sync. Glib::Ex::ConnectProperties is a handy way to link properties between widgets or objects.
It's usually not a good idea to treat a check item's active
property as the "master" storage for a flag, because the row drag-and-drop in Gtk2::TreeView
and similar doesn't work by reordering rows but instead by inserting a copy then deleting the old. MenuView can't tell when that's happening and creates a new item shortly followed by deleting the old, which loses the flag value in the old item.
Radio Button Items
Gtk2::RadioMenuItem
is a subclass of Gtk2::CheckMenuItem
so the above "Check Items" notes apply to it too.
When creating or updating a Gtk2::RadioMenuItem
the "group" is set by passing another radio item widget to group with. Currently there's not much in MenuView to help you find a widget to group with.
Keeping group members in a weakened bucket is one possibility. For top-level rows another is $menuview->get_children
(the Gtk2::Container
method) to find a suitable existing group item. If radio items are all you ever have in the menu then just the first (if any) will be enough.
Calling $menuview->item_at_path
to get another row is no use because you don't want to create new items, only find an existing one. In the future there'll probably be some sort of get current item at path if exists, or get existing items and paths, or get current items in submenu, or get submenu widget, etc.
CellView Items
Gtk2::ComboBox
displays its model-based menus using a Gtk2::CellView
child in each item with Gtk2::CellRenderer
objects for the drawing. Alas it doesn't make this available for general use (only with the selector box, launching from there). You can make a similar thing with MenuView by creating items with a CellView child each.
The only thing to note is that as of Gtk 2.20 a CellView doesn't automatically redraw if the model row changes. item-create-or-update
is called for a row change and from there you can force a redraw with $cellview->set_displayed_row
with the same path already set in it. See examples/cellview.pl for a complete program.
Often a single CellRenderer can be shared among all the items created. Drawing is done one cell at a time so different attribute values applied for different rows don't clash, as long as every CellView sets all attributes which matter. (Is that a documented CellView feature though?)
Buildable
Gtk2::Ex::MenuView
inherits the Gtk2::Buildable
interface like any widget subclass and Gtk2::Builder
can be used to construct a MenuView similar to a plain Gtk2::Menu
. The class name is Gtk2__Ex__MenuView
, so for example
<object class="Gtk2__Ex__MenuView" id="my-menu">
<property name="model">my-model</property>
<signal name="item-create-or-update" handler="my_create"/>
<signal name="activate" handler="my_activate"/>
</object>
Like a plain Gtk2::Menu
, a MenuView will be a top-level object in the builder and then either connected up as the submenu of a menuitem somewhere (in another menu or menubar), or just created ready to be popped up explicitly by event handler code. See examples/builder.pl for a complete program.
Subclassing
If you make a sub-class of MenuView you can have a "class closure" handler for item-create-or-update
and activate
. This is a good way to hide item creation and setups. There's no base class handlers for those signals, so no need to signal_chain_from_overridden
. A subclass might expect certain model columns to contain certain data, like text to display etc.
You can also make a subclass of Gtk2::MenuItem
for the items in a MenuView. This can be a good place to hide code that might otherwise be a blob within item-create-or-update
, perhaps doing things like creating or updating child widgets, etc. MenuView doesn't care what class the items are, as long as they're Gtk2::MenuItem
or some subclass of Gtk2::MenuItem
.
Menu Size
MenuView is intended for up to perhaps a few hundred items. Each item is a separate Gtk2::MenuItem
, usually with a child widget to draw, so it's not particularly memory-efficient. You probably won't want to create huge menus anyway since as of Gtk 2.12 the user scrolling in a menu bigger than the screen is poor. (You have to wait while it scrolls, and if you've got a slow X server it gets badly bogged down by its own drawing.)
FUTURE
It mostly works to $menu->prepend
extra fixed items for the menu (see Gtk2::Menu), not controlled from model rows. For example a Gtk2::TearoffMenuItem
or equivalent of some other class. There's nothing yet to do that in sub-menus though.
It may be possible to append fixed items too. An append
could mean an item after the model ones, and prepend
or an insert
near the start could mean before the model ones. The only tricky bit is when there's no model items yet an insert
right between the prepends and appends would be ambiguous. Perhaps prepend there would be most likely, or have pack_start
and pack_end
style methods.
There's some secret work-in-progress in the code for an optional separator item above each row. The idea is to have visible separators, usually like Gtk2::SeparatorMenuItem
or similar, between sets of related items, perhaps at places where a particular model column value changes.
SEE ALSO
Gtk2::Menu, Gtk2::MenuItem, Gtk2::Label
HOME PAGE
http://user42.tuxfamily.org/gtk2-ex-menuview/index.html
LICENSE
Copyright 2008, 2009, 2010, 2011, 2012, 2017, 2019 Kevin Ryde
Gtk2-Ex-MenuView is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.
Gtk2-Ex-MenuView 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 General Public License for more details.
You should have received a copy of the GNU General Public License along with Gtk2-Ex-MenuView. If not, see http://www.gnu.org/licenses/.