| Gtk2::Ex::WidgetCursor - mouse pointer cursor management for widgets |
Gtk2::Ex::WidgetCursor -- mouse pointer cursor management for widgets
use Gtk2::Ex::WidgetCursor; my $wc = Gtk2::Ex::WidgetCursor->new (widget => $widget, cursor => 'fleur', active => 1);
# show wristwatch while whole program blocked Gtk2::Ex::WidgetCursor->busy;
# bonus invisible cursor creator my $cursor = Gtk2::Ex::WidgetCursor->invisible_cursor;
WidgetCursor manages the mouse pointer cursor shown in widget windows;
ie. the cursor as set by Gtk2::Gdk::Window::set_cursor. A "busy"
mechanism can display a wristwatch on all windows when the whole application
is blocked.
The plain GdkWindow set_cursor lacks even a corresponding get_cursor,
which makes it difficult for add-ons or independent parts of an application
to cooperate with what cursor should be shown at different times or in
various modes. To that end a Gtk2::Ex::WidgetCursor object represents a
desired cursor in one or more widgets. When made "active" and when it's the
newest or highest priority then the specified cursor is set onto those
widget window(s). If the WidgetCursor object is later made inactive or
destroyed then the next highest WidgetCursor takes effect, etc.
The idea is to have say a base WidgetCursor for an overall widget mode, then something else temporarily while dragging, an perhaps a wristwatch "busy" indication trumping one or both (like the global "busy" mechanism below).
The examples subdirectory in the sources has some variously contrived example programs.
Gtk2::Ex::WidgetCursor->new (key => value, ...)
Create a new WidgetCursor object. Parameters are taken in key/value
style,
widget single widget
widgets array reference for multiple widgets
cursor string or object
active boolean
priority default 0
include_children boolean
For example,
$wc = Gtk2::Ex::WidgetCursor->new (widget => $widget,
cursor => 'fleur',
active => 1);
cursor can be any of
A Gtk2::Gdk::Cursor object. If your program uses multiple displays then
remember the cursor object must be from the same display as the widget(s).
A string name of a cursor from the Gtk2::Gdk::CursorType enum, such as
"hand1" (see the Gtk2::Gdk::Cursor manpage for the full list).
The special string name "invisible" to have no cursor at all.
undef to inherit the parent window's cursor, which often means the
default little pointing arrow of the root window.
active can be set to make the new cursor take effect immediately,
otherwise the active() function below turns it on when desired.
include_children means all the children of the given widgets are affected
too. Normally the cursor in a child widget overrides anything in its
parents (the way set_cursor does at the window level). But with
include_children a setting in a parent applies to the children too, with
priority level and newest applied as usual.
Optional priority is a number. The default is level 0 and higher values
are higher priority. A low value (ie. negative) can act as a fallback, or a
high value can trump other added cursors.
$wc->active ([$newval])
Get or set the "active" state of $wc.
$wc->cursor ([$cursor])
Get or set the cursor of $wc. Any cursor setting in the style of new
above can be given. Eg.
$wc->cursor ('umbrella');
$wc->widgets ()
Return the widgets currently in $wc. Eg.
my @array = $wc->widgets;
or if you know you're only acting on one widget then say
my ($widget) = $wc->widgets;
$wc->add_widgets ($widget, $widget, ...)
Add widgets to $wc. Any widgets already in $wc are ignored.
WidgetCursor objects can operate on unrealized widgets. The cursor settings take effect if/when the widgets are realized.
A WidgetCursor object only keeps weak references to its widget(s), so the mere fact there's a desired cursor won't keep them alive forever. Garbage collected widgets drop out of the widgets list set. In particular this means it's safe to hold a WidgetCursor within a widget's own hash without creating a circular reference. Eg.
my $widget = Gtk2::DrawingArea->new;
$widget->{'base_cursor'} = Gtk2::Ex::WidgetCursor->new
(widget => $widget,
cursor => 'hand1',
active => 1,
priority => -10);
busy is a global mechanism setting a watch cursor on all windows to tell
the user the program is doing CPU-intensive work and might not iterate the
main loop to draw or interact for a while.
If your busy state isn't CPU-intensive, but instead say waiting for a timer
or a read from a socket, then this is not what you want, it'll turn off too
soon. (Instead simply make a WidgetCursor with a "watch" and turn it
on or off at your start and end points; see for instance
examples/timebusy.pl in the sources.)
Gtk2::Ex::WidgetCursor->busy ()
Show the "watch" cursor (a little wristwatch) in all the application's
current toplevel and popup windows. An idle handler
(Glib::Idle->add) removes the watch automatically upon returning to
the main loop.
The X queue is flushed to set the cursor immediately, so the program can go straight into its work. For example
Gtk2::Ex::WidgetCursor->busy;
foreach my $i (1 .. 1_000_000) {
# do much number crunching
}
busy uses a WidgetCursor object as described above and so cooperates
with application uses of that. Priority level 1000 is set to trump other
cursor settings.
Gtk2::Ex::WidgetCursor->unbusy ()
Explicitly remove the watch cursor setup by busy above. The X request
queue is flushed to ensure any cursor change appears immediately. If
busy is not active then do nothing.
It's unlikely you'll need unbusy, because if your program hasn't yet
reached the idle handler in the main loop then it's probably still busy!
But perhaps if most of your work is done then you could unbusy while the
remainder is finishing up.
Currently if you open a new toplevel window while in a busy then you must
call Gtk2::Ex::WidgetCursor->busy () a second time to make that new
window show the wristwatch. Perhaps that can be done automatically in the
future, since the intention of busy is to cover all application windows.
The following is the "invisible" cursor used by WidgetCursor above, made
available for general use. Gtk has code for this in GtkEntry and
GtkTextView, but as of Gtk 2.12 doesn't make it available to
applications.
Gtk2::Ex::WidgetCursor->invisible_cursor ([$target])
Return a Gtk2::Gdk::Cursor object which is invisible, ie. displays no
cursor at all. This is the sort of "no pixels set" cursor described in the
Gtk reference manual (under gdk_cursor_new for instance).
With no arguments (or undef) the cursor is for the default display per
Gtk2::Gdk::Display->get_default. If your program only uses one
display then that's all you need.
my $cursor = Gtk2::Ex::WidgetCursor->invisible_cursor;
For multiple displays note that a cursor is a per-display resource, so you
must pass a $target. This can be a Gtk2::Gdk::Display itself or
anything with a get_display method, which includes Gtk2::Widget,
Gtk2::Gdk::Window (or any Gtk2::Gdk::Drawable), another
Gtk2::Gdk::Cursor, etc.
my $cursor = Gtk2::Ex::WidgetCursor->invisible_cursor ($widget);
When passing a widget as the target note the display comes from its toplevel
Gtk2::Window parent, so the widget must have been added in as a child
somewhere under a toplevel (or be a toplevel itself of course). Until then
get_display returns undef and invisible_cursor will croak.
The invisible cursor is cached against the display, so repeated calls don't make a new one every time.
WidgetCursor settings are applied to the widget windows without paying attention to which among them are "no-window" and thus using their parents' windows. If different no-window children have a common windowed parent then WidgetCursor won't notice and the result will probably come out wrong. For now it's suggested you either always give a windowed widget, or at least always the same no-window child.
In the future it might be possible to have cursors on no-window widgets with
WidgetCursor using enter/leave the same way Gtk2::LinkButton does for its
hand cursor. But windowed widgets are best for cursor settings normally,
since they let the X server take care of the cursor as the mouse moves
around.
Reparenting widgets subject to an include_children probably doesn't quite
work. If it involves a new realize it probably works, otherwise probably
not. Moving widgets is unusual, so in practice this isn't too bad. Doing
the right thing in all cases might need a lot of add or parent signal
connections.
Widgets doing Gtk2::Gdk::Window::set_cursor themselves generally defeat
the WidgetCursor mechanism. WidgetCursor has some special handling for
Gtk2::Entry and Gtk2::TextView (their insertion point cursor), but a
few other core widgets have problems. The worst affected currently is
Gtk2::LinkButton. Hopefully this will improve in the future, though the
ill effects may be as little as an include_children not in fact
"including" children of the offending types.
the Gtk2::Gdk::Cursor manpage, the Gtk2::Widget manpage, the Gtk2::Gdk::Window manpage, the Gtk2::Gdk::Display manpage
| Gtk2::Ex::WidgetCursor - mouse pointer cursor management for widgets |