NAME
Gtk2::Ex::Dragger -- drag to move adjustment position
SYNOPSIS
use Gtk2::Ex::Dragger;
Gtk2::Ex::Dragger->new (widget => $widget,
hadjustment => $widget->get_hadjustment);
DESCRIPTION
Gtk2::Ex::Dragger
implements mouse pointer dragging to move the contents of a widget horizontally, vertically, or both. It works on any windowed widget which has Gtk2::Adjustment
objects controlling the visible part.
The width or height of the widget corresponds to the "page" in the adjustment. Dragger scales pixel movement onto the adjustment "value" accordingly. It's then up to the usual widget drawing to follow value-changed
signals from the adjustment for redraws, the same as for scrollbars etc. The effect for the user is that the contents are pulled around with the mouse.
Adjustment
+--+ --upper
| |
Window | |
+-------------+ \ | |
| | \| |
| | +--+ \_ page size
| | | | /
| | +--+ ___ value
| | /| |
+-------------+ / | |
| |
| |
+--+ --lower
If you've got scrollbars then they move with the dragging too. It can be good to have both ways of moving since the scrollbars give visual feedback but dragging allows finer movements if the visible page is a very small part of the total adjustable extent.
The "confine" option lets you to restrict mouse movement to screen positions corresponding to the adjustment upper/lower, so the user gets an obvious feedback at the limits.
The "cursor" option changes the mouse pointer cursor while dragging. This is good if it's not clear for a given widget which button press etc activates a drag. The cursor is set through WidgetCursor (see Gtk2::Ex::WidgetCursor) and so cooperates with other uses of that. See examples/heart.pl in the Gtk2-Ex-Dragger sources, or examples/busy.pl for the global "busy" indication.
Dragger can work on both natively scrollable widgets and widgets put into a Gtk2::Viewport
. For a viewport it's the viewport widget which is passed to the dragger since that's the widget showing a portion of a larger underlying thing. (Eg. examples/textview.pl natively or examples/label.pl and examples/layout.pl in a Viewport or Layout, and examples/vruler.pl with separately jigged up adjusters.)
Changes to the adjustment value, upper/lower, page size, or the widget window size are all allowed during a drag. A change to the value could come from a keyboard page-up etc. In all cases the Dragger continues relative to the new position and will update any "confine" window limits.
Event Mask
The dragger adds the following events mask bits to the target widget. It uses the WidgetEvents mechanism (see Gtk2::Ex::WidgetEvents) so they're cleaned up if the dragger is destroyed.
button-press-mask
button-motion-mask
button-release-mask
The dragger doesn't itself have a button press handler (at present), rather an application is expected to start the drag for some button/modifier combination. The dragger adds the press mask in readiness for that.
If you want to defer dragger creation until actually needed in a button press then you must explicitly select the motion and release events beforehand. For example,
# events selected beforehand
$widget->add_events (['button-press-mask',
'button-motion-mask',
'button-release-mask']);
# dragger not created until actually wanted
sub my_button_press_handler {
my ($widget, $event) = @_;
if (some_thing()) {
require Gtk2::Ex::Dragger;
$dragger = Gtk2::Ex::Dragger->new (...);
$dragger->start ($event);
}
}
The motion and release masks beforehand are important if the application is lagged. It's possible the user has already released when the application receives the press. If the release mask wasn't already on then the release event is not generated. If you forget those masks then currently the dragger turns on but then doesn't work and won't turn off (either all the time, or when lagged, or the first use).
FUNCTIONS
Gtk2::Ex::Dragger->new (key=>value, ...)
-
Create and return a new dragger. Key/value pairs set the following various parameters,
widget the widget to drag hadjustment Gtk2::Adjustment vadjustment Gtk2::Adjustment hinverted boolean vinverted boolean cursor cursor name per Gtk2::Ex::WidgetCursor confine boolean update_policy string (see UPDATE POLICY below)
The target
widget
and at least one ofhadjustment
orvadjustment
are mandatory, the rest are options.The
hinverted
orvinverted
flags swap the direction the adjustments are moved. Normallyhadjustment
increases to the left andvadjustment
increases upwards. Inverting goes instead to the right or downwards. This is the same sense as inverted onGtk2::Scrollbar
, so if you setinverted
on the scrollbar then do the same to the dragger.cursor
is any cursor name or object accepted by the WidgetCursor mechanism (see Gtk2::Ex::WidgetCursor). If unset orundef
(the default) then the cursor is unchanged and you don't need WidgetCursor installed in that case. $dragger->start ($event)
-
Begin a drag.
$event
must be aGtk2::Gdk::Event::Button
object; it gives the button doing the drag and the server timestamp. $dragger->stop ()
$dragger->stop ($event)
-
Stop
$dragger
, if it's active. Normally a dragger stops by itself when the dragging button is released, but this method can be do it sooner.If you stop in response to a
Gtk2::Gdk::Event
then pass that so its timestamp can be used. This matters when the dragger uses an active grab. If application event processing is a bit lagged the timestamp ensures the ungrab doesn't kill a later passive grab on a button press or an explicit grab by another client.
PROPERTIES
widget
(Gtk2::Widget
, defaultundef
)-
The widget whose contents are to be dragged around.
Currently if
widget
is changed while a drag is in progress then the drag is stopped. In the future it may be possible to switch, though doing so would be a bit unusual. hadjustment
(Gtk2::Adjustment
object, defaultundef
)vadjustment
(Gtk2::Adjustment
object, defaultundef
)-
The adjustment objects representing the current position and range of movement in the respective directions. Nothing will move until at least one of these two is set.
hinverted
(boolean, default false)vinverted
(boolean, default false)-
Swap the direction the respective adjustments are moved. Normally
hadjustment
increases to the left andvadjustment
increases upwards. Inverting goes instead to the right or downwards. These are the same way around as theGtk2::Scrollbar
inverted
property so if you setinverted
on the scrollbar then do the same to the dragger. cursor
(scalar, defaultundef
)cursor-name
(string, cursor enum nick or "invisible", defaultundef
)cursor-object
(Gtk2::Gdk::Cursor
, defaultundef
)-
cursor
is any cursor name or object accepted by the WidgetCursor mechanism (see Gtk2::Ex::WidgetCursor). If unset orundef
(the default) then the cursor is unchanged and you don't need WidgetCursor installed in that case.The
cursor-name
andcursor-object
properties access the same underlyingcursor
setting but with respective string or cursor object type. They can be used from aGtk2::Builder
specification. confine
(boolean, default false)-
Whether to confine the user's mouse movement to the screen area corresponding to the adjustment upper/lower ranges.
update-policy
(UpdatePolicy enum, default "sync")-
See "UPDATE POLICY" above.
UPDATE POLICY
The update_policy
option (a string) controls how often value-changed
signals are emitted on the adjustments. The dragger always stores updated values in the adjustments immediately (and emits notify
), but it can be configured to defer the value-changed
signal. This is similar to the way scrollbars work (see Gtk2::Scrollbar) and the possible settings are similar.
"continuous"
-
value-changed
is emitted on every motion event. "delayed"
-
value-changed
is emitted 250 milliseconds after a change. "discontinuous"
-
value-changed
is not emitted at all during the drag, only at the end (button release orstop
function). - secret default policy
-
value-changed
is emitted after a sync with the server (implemented without blocking, see Gtk2::Ex::SyncCall) followed by reaching idle in the main loop, or at a 250 ms timeout if idle is not reached.This is designed to be a compromise between smoothness and excessive drawing. The sync avoids hammering the server, then the idle waits to avoid excessive work on the client side, but with the timeout cutting it short to guarantee updates are not deferred indefinitely.
Choosing a policy is a matter of how good the drawing in your target widget is. You can see the difference in the example programs included in the sources which draw a big block of text in a Gtk2::TextView
versus a Viewport plus Gtk2::Label
. The TextView goes very close to coping with continuous
update policy, but the same on the Label's simple-minded drawing floods the server to the point of being unusable.
Dragger recognises pointer-motion-hint-mask
on the target widget (or rather the motion event is_hint
) and knows to do a $widget->get_pointer
for current position and further events. That's a deliberate server round-trip on each move, with the effect that each motion waits until the drawing etc from the previous one has finished. Generally you can set update_policy
to continuous
in this case. Give it a try if you're having trouble with excessive drawing or excessive network traffic with full motion events. For the drawing, the Dragger default update_policy
is meant to achieve the same effect asynchronously.
It's a bit unfortunate that an update policy is part of a controller like a scrollbar or dragger. It'd be better if redraw frequency were left to the widgets which are actually redrawing; or at least to an adaptor like a Viewport for those without their own understanding.
OTHER NOTES
Some good choices for the cursor
while dragging are
fleur 4-way arrow
double-arrow horizontal 2-way
sb-h-double-arrow horizontal 2-way
sb-v-double-arrow vertical 2-way
There's not much in the standard cursors for a grasping hand so you probably have to make something like that from a pixmap.
Currently only a weak reference is kept to the target widget, so the fact there's a dragger feature doesn't keep it alive forever. This means in particular it's safe to hold the dragger object in the widget instance data without creating a circular reference. But strong references are kept to the adjustment objects since they probably should stay alive as long as the widget and dragger do. But perhaps this will change.
Having the button-motion-mask
and button-release-mask
set before the drag won't normally cost very many extra events.
A missed release event can't be properly handled after the fact. A get_pointer
can say whether the button is now down, but it may be the user pressing elsewhere, and the x,y position of the drag release has been completely lost. That final release position is quite important. If the application is lagged you still want press/drag/release to move the widget contents by the corresponding distance. It's wrong and quite annoying if the contents jump to where the mouse has gone after release. The scrollbars in some versions of mozilla for instance do that wrongly.
It'd be possible for the dragger to turn on button-motion-mask
when starting the drag, if not already on, using an active grab and a $display->get_pointer
to check for any missed movement. But for now that doesn't seem worthwhile, not while the release mask can't be similarly adapted.
BUILDABLE
Gtk2::Ex::Dragger
can be built in a Gtk2::Builder
spec the same as any other Glib::Object
. The class name is "Gtk2__Ex__Dragger" as usual for Perl-Gtk package name to class name conversion. The target widget
, hadjustment
and vadjustment
properties can be set to objects created in the spec, for example
<object class="Gtk2__Ex__Dragger" id="dragger">
<property name="widget">viewport</property>
<property name="hadjustment">hadjustment</property>
<property name="vadjustment">vadjustment</property>
</object>
See examples/builder.pl in the Gtk2-Ex-Dragger sources for a complete program. Or examples/builder-internal.pl shows how to connect up to adjustment objects offered as "internal children" of a widget (which is a handy way to expose what a widget creates, though no Gtk core widgets do it).
SEE ALSO
Gtk2::Adjustment, Gtk2::Ex::WidgetCursor, Gtk2::Viewport, Gtk2::ScrolledWindow
HOME PAGE
http://user42.tuxfamily.org/gtk2-ex-dragger/index.html
LICENSE
Copyright 2007, 2008, 2009, 2010, 2011 Kevin Ryde
Gtk2-Ex-Dragger 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-Dragger 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-Dragger. If not, see http://www.gnu.org/licenses/.