Gtk2::Ex::WidgetCursor -- mouse pointer cursor management for widgets
use Gtk2::Ex::WidgetCursor; my $wc = Gtk2::Ex::WidgetCursor->new (widget => $mywidget, cursor => 'fleur', active => 1); # show wristwatch everywhere while number crunching Gtk2::Ex::WidgetCursor->busy; # bonus invisible cursor creator my $cursor = Gtk2::Ex::WidgetCursor->invisible_cursor;
Gtk2::Ex::WidgetCursor is a subclass of Glib::Object.
Gtk2::Ex::WidgetCursor
Glib::Object
Glib::Object Gtk2::Ex::WidgetCursor
WidgetCursor manages the mouse pointer cursor shown in widget windows as per Gtk2::Gdk::Window set_cursor. A "busy" mechanism can display a wristwatch in all windows when the whole application is blocked.
Gtk2::Gdk::Window
set_cursor
With the plain window set_cursor, it's difficult for widget add-ons or independent parts of an application to cooperate with what should be shown at different times or in different modes.
A Gtk2::Ex::WidgetCursor object represents a desired cursor in one or more widgets. When "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 remaining highest WidgetCursor takes effect, etc.
The idea is to have say a base WidgetCursor for an overall mode, then something else temporarily while dragging, and perhaps a wristwatch "busy" indication overriding one or both those (like the global "busy" mechanism below).
See the examples directory in the sources for some variously contrived sample programs.
$wc = Gtk2::Ex::WidgetCursor->new (key => value, ...)
Create and return a new WidgetCursor object. Optional key/value parameters set initial properties as per Glib::Object->new (see Glib::Object).
WidgetCursor
Glib::Object->new
$wc = Gtk2::Ex::WidgetCursor->new (widget => $mywidget, cursor => 'fleur', active => 1);
Note that active is false by default and the WidgetCursor does nothing to the widgets until made active by the property or the method call below.
active
WidgetCursor objects can be applied to unrealized widgets. The cursor settings take effect if/when the widgets are realized.
$bool = $wc->active ()
$wc->active ($newval)
Get or set the "active" state of $wc. This is the active property.
$wc
$cursor = $wc->cursor ()
$wc->cursor ($cursor)
Get or set the cursor of $wc. This is the cursor property, see "PROPERTIES" below for possible values. Eg.
cursor
$wc->cursor ('umbrella');
@widgets = $wc->widgets ()
Return a list of 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. Those not already in $wc are added.
widget
Gtk2::Widget
undef
widgets
The widget or widgets to act on.
A WidgetCursor object only keeps weak references to its widget(s), so the mere fact there's a desired cursor won't keep a widget alive forever. Garbage collected widgets drop out of the widgets list. 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);
add-widget
Add a widget to those to act on. This is a write-only pseudo-property calling add_widget above. It's good for Gtk2::Builder where the widgets property can't be used since it's a Perl scalar type.
add_widget
Gtk2::Builder
Gtk2::Gdk::Cursor
The cursor to show in the widgets. This can be
A string cursor type nick from the Gtk2::Gdk::CursorType enum, such as "hand1". See Gtk2::Gdk::Cursor for the full list of cursor types.
Gtk2::Gdk::CursorType
"hand1"
Special string name "invisible" to have no cursor at all. In Gtk 2.16 up this is the same as "blank-cursor", or in earlier versions gives a "no pixels set" pixmap cursor.
"invisible"
A Gtk2::Gdk::Cursor object.
If your program uses multiple displays then remember the cursor object must be on the same display (ie. Gtk2::Gdk::Display) as the widget(s). If you have more than one widget then they must be all on the same display in this case. (For a named cursor they don't have to be.)
Gtk2::Gdk::Display
undef to inherit the parent window's cursor, which may be the default little pointing arrow or whatever from the root window.
cursor-name
cursor-object
The cursor to show in the widgets, as a plain Glib string or object property. These are designed for use from Gtk2::Builder where the scalar type cursor property can't be set.
Reading from cursor-name when a cursor object has been set gives the type nick if it has one, or if it's a pixmap then currently undef.
Reading from cursor-object when a cursor name string has been set gives undef currently. It'd be possible to make or return the cursor object in use (or which will be used when realized) but that doesn't seem worth bothering with as yet.
Whether to apply the cursor to the widgets. This can be set at any time, including before widgets are added or before they're realized. Widgets added get the setting when realized.
priority
The priority level of this WidgetCursor among multiple WidgetCursors acting on a widget.
Higher values are higher priority. A low value (perhaps negative) can act as a fallback, or a high value can be above other added cursors.
include-children
Whether to apply the cursor to child widgets of the given widgets too. Normally the cursor in a child widget overrides its parents (as set_cursor does at the window level). But with include-children, a setting in a parent applies to the children too, with priority+newest applied as usual.
The busy mechanism sets a "watch" cursor on all windows to tell the user the program is doing CPU-intensive work and might not run the main loop to draw or interact for a while.
busy
If your busy state isn't CPU-intensive, but instead perhaps a Glib timer or an I/O watch on 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 examples/timebusy.pl for that sort of thing.)
"watch"
Gtk2::Ex::WidgetCursor->busy ()
Show the "watch" cursor (a little wristwatch) in all the application's widget windows (toplevels, dialogs, popups, etc). An idle handler (Glib::Idle->add) removes the watch automatically upon returning to the main loop.
Glib::Idle->add
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 }
If you create new windows within a busy then they too get the busy cursor (or they're supposed to, something fishy in Gtk 2.20 and maybe 2.18 has broken it). You can even go busy before creating any windows at all. But note WidgetCursor doesn't do any extra X flush for new creations; if you want them to show immediately then you must flush in the usual way.
busy uses a WidgetCursor object as described above and so cooperates with application uses of that. Priority level 1000 is set to be above 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.
unbusy
The following invisible cursor is used by WidgetCursor for the "invisible" cursor and is made available for general use.
$cursor = Gtk2::Ex::WidgetCursor->invisible_cursor ()
$cursor = Gtk2::Ex::WidgetCursor->invisible_cursor ($target)
Return a Gtk2::Gdk::Cursor object which is invisible, ie. displays no cursor at all. This is the blank-cursor in Gtk 2.16 and up, or for earlier versions a "no pixels set" cursor as described by gdk_cursor_new().
blank-cursor
gdk_cursor_new()
With no arguments (or undef) the cursor is for the default display Gtk2::Gdk::Display->get_default. If your program only uses one display then that's all you need.
Gtk2::Gdk::Display->get_default
my $cursor = Gtk2::Ex::WidgetCursor->invisible_cursor;
For multiple displays, a cursor is a per-display resource so you must pass a $target. This can be a Gtk2::Gdk::Display, or anything with a get_display method, including Gtk2::Widget, Gtk2::Gdk::Window, Gtk2::Gdk::Drawable, another Gtk2::Gdk::Cursor, etc.
$target
get_display
Gtk2::Gdk::Drawable
my $cursor = Gtk2::Ex::WidgetCursor->invisible_cursor($widget);
When passing a widget, note the display comes from its toplevel Gtk2::Window parent and until added as a child somewhere under a toplevel window its get_display is the default display and invisible_cursor will give a cursor for that display.
Gtk2::Window
invisible_cursor
The invisible cursor is cached against the display so repeated calls don't make a new one every time.
Gtk had its own "no pixels set" cursor constructor code in GtkEntry and GtkTextView prior to "blank-cursor" but didn't make it available to applications.
GtkEntry
GtkTextView
WidgetCursor is a Glib::Object and can be created by Gtk2::Builder in the usual way. Each is a separate toplevel object and the widget property sets what it should act on. The add-widget pseudo-property allows multiple widgets to be set.
<object class="Gtk2__Ex__WidgetCursor" id="wcursor"> <property name="widget">mywidget</property> <property name="active">1</property> </object>
See examples/builder.pl and examples/builder-add.pl for complete sample programs.
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.
An exception to the no-window rule is Gtk2::Button. It has the no-window flag but in fact keeps a private input-only event window over its allocated space. WidgetCursor digs that out and uses it to put the cursor on the intended area. But an exception to this exception is Gtk2::LinkButton where a setting on the button works fine, but any WidgetCursor on its parent widget is messed up.
Gtk2::Button
Gtk2::LinkButton
In the future, it might be possible to have cursors on no-window widgets by following motion-notify events within the container parent in order to update the cursor as it goes across different children. Something similar might allow certain regions of a window to have a particular cursor, such as hyperlinked clickable text. But windowed widgets are normally best, since they let the X server take care of the display immediately as the mouse moves around.
motion-notify
Reparenting widgets under an include_children probably doesn't quite work. If it involves a new realize then it may work (such as reparenting to a different screen). 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.
include_children
add
parent
Widgets calling $window->set_cursor themselves generally don't work with 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 currently is Gtk2::LinkButton per above. The ill effects are often as modest as an include_children not working on children of offending types.
$window->set_cursor
Gtk2::Entry
Gtk2::TextView
Gtk2::Gdk::Cursor, Gtk2::Widget, Gtk2::Gdk::Window, Gtk2::Gdk::Display
Wx::BusyCursor
http://user42.tuxfamily.org/gtk2-ex-widgetcursor/index.html
Copyright 2007, 2008, 2009, 2010, 2011, 2012, 2017, 2019 Kevin Ryde
Gtk2-Ex-WidgetCursor 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-WidgetCursor 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-WidgetCursor. If not, see http://www.gnu.org/licenses/.
To install Gtk2::Ex::WidgetCursor, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Gtk2::Ex::WidgetCursor
CPAN shell
perl -MCPAN -e shell install Gtk2::Ex::WidgetCursor
For more information on module installation, please visit the detailed CPAN module installation guide.