Gtk2-Ex-Dragger
view release on metacpan or search on metacpan
lib/Gtk2/Ex/Dragger.pm view on Meta::CPAN
my ($self) = @_;
$self->stop;
}
sub GET_PROPERTY {
my ($self, $pspec) = @_;
my $pname = $pspec->get_name;
### Dragger GET_PROPERTY(): $pname
if ($pname eq 'cursor_name') {
my $cursor = $self->{'cursor'};
if (Scalar::Util::blessed($cursor)) {
$cursor = $cursor->type;
# think prefer undef over cursor-is-pixmap for the get()
if ($cursor eq 'cursor-is-pixmap') {
undef $cursor;
}
}
return $cursor;
}
if ($pname eq 'cursor_object') {
my $cursor = $self->{'cursor'};
return (Scalar::Util::blessed($cursor)
&& $cursor->isa('Gtk2::Gdk::Cursor')
&& $cursor);
}
return $self->{$pname};
}
sub SET_PROPERTY {
my ($self, $pspec, $newval) = @_;
### Dragger SET_PROPERTY(): $pspec->get_name
my $pname = $pspec->get_name;
my $pname_is_cursor = ($pname =~ s/^(cursor).*/$1/);
my $oldval = $self->{$pname};
$self->{$pname} = $newval;
if ($pname_is_cursor) {
# copy boxed GdkCursor in case the caller frees it, and in particular
# for $pname eq 'cursor_object' it might be freed immediately by the
# GValue call-out stuff
# if (blessed($newval) && $newval->isa('Gtk2::Gdk::Cursor')) {
# $self->{$pname} = $newval->copy;
# }
_widget_cursor($self);
$self->notify('cursor');
$self->notify('cursor-name');
$self->notify('cursor-object');
} elsif ($pname eq 'widget') {
my $widget = $newval;
if (! $newval
|| ($oldval && $newval != $oldval)) {
# ENHANCE-ME: might be able to switch to an active grab on the new
# widget and continue
$self->stop;
}
if ($widget) {
Scalar::Util::weaken ($self->{'widget'});
}
$self->{'wevents'} = $widget && Gtk2::Ex::WidgetEvents->new
($widget, ['button-press-mask',
'button-motion-mask',
'button-release-mask']);
_widget_signals($self);
} elsif ($pname =~ /([hv])(adjustment|inverted)/) {
my $axis = $self->{$1};
my $field = $2;
$axis->{$field} = $newval;
if ($field eq 'adjustment') {
$axis->{'last_value'} = $newval && $newval->value;
_axis_signals($self,$axis);
_do_adjustment_changed ($newval, \$self);
}
} elsif ($pname eq 'confine') {
_resize_confine_win ($self);
}
}
# create or update WidgetCursor according to $self->{'cursor'}
# doesn't load WidgetCursor until drag active
sub _widget_cursor {
my ($self) = @_;
$self->{'wcursor'} = ($self->{'active'}
&& $self->{'cursor'}
&& do {
require Gtk2::Ex::WidgetCursor;
Gtk2::Ex::WidgetCursor->new
(widget => $self->{'widget'},
cursor => $self->{'cursor'},
active => 1);
});
}
# update $self->{'h'}->{'adjustment_ids'} or $self->{'v'}->... signal handlers
# handlers only applied while 'active'
sub _axis_signals {
my ($self, $axis) = @_;
my $adj;
$axis->{'adjustment_ids'} =
($self->{'active'}
&& ($adj = $axis->{'adjustment'})
&& do {
my $ref_weak_self = _ref_weak ($self);
### _axis_signals() connect
Glib::Ex::SignalIds->new
($adj,
$adj->signal_connect (changed => \&_do_adjustment_changed,
$ref_weak_self),
$adj->signal_connect (value_changed => \&_do_adjustment_value_changed,
$ref_weak_self))
});
}
# make signal handler connections on $self->{'widget'} if active
lib/Gtk2/Ex/Dragger.pm view on Meta::CPAN
#
# GtkLayout, and subclasses like GnomeCanvas
# Plain $widget->window is the visible extent, so nothing special
# needed. The scrolls move the bin_window subwindow, but how scrolling
# is drawn doesn't matter to us.
#
sub Gtk2::Widget::Gtk2_Ex_Dragger_window {
my ($widget) = @_;
if (exists $widget->{'Gtk2_Ex_Dragger_window'}) {
# user override -- but this not (yet) a documented feature as such
return $widget->{'Gtk2_Ex_Dragger_window'};
}
# default
return $widget->window;
}
# for TextView the "text" window is the visible extent
sub Gtk2::TextView::Gtk2_Ex_Dragger_window {
my ($textview) = @_;
return $textview->get_window ('text');
}
# for TreeView the "bin" window is the visible extent
*Gtk2::TreeView::Gtk2_Ex_Dragger_window
= \&Gtk2::TreeView::get_bin_window;
# For Viewport there's $widget->window then within that a "view_window"
# which is smaller by the border size. This view_window is the scrollable
# part we're interested in, but it's not a documented feature, so this is a
# nasty hack to pick it out.
#
# The $viewport->get_bin_window is a sub-window of the view_window. It
# contains the viewport children. (In the gtk manual in gtk 2.20 but not
# explained as such.)
#
sub Gtk2::Viewport::Gtk2_Ex_Dragger_window {
my ($viewport) = @_;
my $win;
return (($win = $viewport->window) # if realized
&& ($win->get_children)[0]);
}
#------------------------------------------------------------------------------
# generic helpers
# Return two values ($floor, $frac).
# $floor is $x rounded down to an integer towards negative infinity
# $frac is the fractional part subtracted from $x to get to $floor,
# so $floor+$frac == $x
#
sub _floor_and_frac {
my ($x) = @_;
my $f = POSIX::floor ($x);
return ($f, $x - $f);
}
sub _ref_weak {
my ($self) = @_;
Scalar::Util::weaken ($self);
return \$self;
}
#------------------------------------------------------------------------------
1;
__END__
=for stopwords Gtk2-Ex-Dragger Dragger scrollbars WidgetCursor natively viewport dragger scrollbar timestamp Gdk ungrab Viewport TextView pixmap Ryde WidgetEvents boolean enum UpdatePolicy mozilla
=head1 NAME
Gtk2::Ex::Dragger -- drag to move adjustment position
=for test_synopsis my ($widget)
=head1 SYNOPSIS
use Gtk2::Ex::Dragger;
Gtk2::Ex::Dragger->new (widget => $widget,
hadjustment => $widget->get_hadjustment);
=head1 DESCRIPTION
C<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 C<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
C<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.
( run in 1.856 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )