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 and 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 fine movements if the visible page is a very small part of the adjustment extent.

The "confine" option lets you to restict mouse movement to screen positions corresponding to the adjustment extents, 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 quite 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 (like its 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 larger underlying thing.

Changes to the adjustment value, extents, page size, or the widget window size are all allowed during a drag. A change to the value for example could be from a keyboard page-up etc. In all cases the Dragger continues relative to the new position, including updating the "confine" limits.

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 of hadjustment or vadjustment are mandatory, the rest are options.

The hinverted or vinverted flags swap the direction the adjustments are moved. Normally hadjustment increases to the left and vadjustment increases upwards. Inverting goes instead to the right, and downwards. This is the same as Gtk2::Scrollbar, so if you've set inverted on your 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 or undef (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 a Gtk2::Gdk::Event::Button object; it gives the button doing the drag and a 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 Gdk event then pass that Gtk2::Gdk::Event so its timestamp can be used. (This matters under the "confine" option since it's implemented by an explicit grab. If application event processing is a bit lagged a timestamp ensures the ungrab doesn't kill a later button press passive grab or an explicit grab by another client.)

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 processed.

"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 or stop 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 on 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 naive 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 a fleur 4-way arrow, or double-arrow or sb-h-double-arrow for horizontal 2-way, and sb-v-double-arrow for 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.

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 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/.