Kevin Ryde > Gtk2-Ex-Dragger-10 > Gtk2::Ex::Dragger

Download:
Gtk2-Ex-Dragger-10.tar.gz

Dependencies

Annotate this POD

Website

View/Report Bugs
Module Version: 10   Source  

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 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 or downwards. This is the same sense as inverted on Gtk2::Scrollbar, so if you set inverted 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 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 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, default undef)

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, default undef)
vadjustment (Gtk2::Adjustment object, default undef)

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 and vadjustment increases upwards. Inverting goes instead to the right or downwards. These are the same way around as the Gtk2::Scrollbar inverted property so if you set inverted on the scrollbar then do the same to the dragger.

cursor (scalar, default undef)
cursor-name (string, cursor enum nick or "invisible", default undef)
cursor-object (Gtk2::Gdk::Cursor, default undef)

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.

The cursor-name and cursor-object properties access the same underlying cursor setting but with respective string or cursor object type. They can be used from a Gtk2::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 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 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/.

syntax highlighting: