Gtk2-Ex-WidgetCursor

 view release on metacpan or  search on metacpan

lib/Gtk2/Ex/WidgetCursor.pm  view on Meta::CPAN

#
sub _container_recursively {
  my @pending = @_;
  my @ret;
  while (@pending) {
    my $widget = pop @pending;
    push @ret, $widget;
    if (my $func = $widget->can('get_children')) {
      push @pending, $widget->$func;
    }
  }
  return @ret;
}

#------------------------------------------------------------------------------
# operative windows hacks
#
# $widget->Gtk2_Ex_WidgetCursor_windows() returns a list of windows in
# $widget to act on, with hacks to pickup multiple windows on core classes.
#
# $widget->Gtk2_Ex_WidgetCursor_hack_restore() returns ($win, $cursor).
# $cursor is a string cursor name to put back on $win when there's no more
# WidgetCursor objects.  Or the return is an empty list or $win undef when
# nothing to hack (in which case all windows go back to "undef" cursor).
#

# default to operate on $widget->window alone
*Gtk2::Widget::Gtk2_Ex_WidgetCursor_windows = \&Gtk2::Widget::window;
sub Gtk2::Widget::Gtk2_Ex_WidgetCursor_hack_restore { return (); }

# GtkEventBox under a GtkComboBox popup window has a 'top-left-arrow'.  It
# gets overridden by a special case in the recursive updates above, and
# hack_restore() here puts it back.
#
sub Gtk2::EventBox::Gtk2_Ex_WidgetCursor_hack_restore {
  my ($widget) = @_;
  return _widget_is_combo_eventbox($widget)
    && ($widget->window, 'top-left-arrow');
}

# GtkTextView operate on 'text' subwindow to override its insertion point
# cursor there, plus the main 'widget' window to cover the entire widget
# extent.  The 'text' subwindow insertion point is supposed to be on when
# the widget is sensitive, so hack_restore() that.
#
sub Gtk2::TextView::Gtk2_Ex_WidgetCursor_windows {
  my ($widget) = @_;
  return ($widget->get_window ('widget'),
          $widget->get_window ('text'));
}
sub Gtk2::TextView::Gtk2_Ex_WidgetCursor_hack_restore {
  my ($widget) = @_;
  return $widget->sensitive && ($widget->get_window('text'), 'xterm');
}

# GtkEntry's extra subwindow is included here.  And when sensitive it should
# be put back to an insertion point.  For a bit of safety use list context
# etc to allow for no subwindows, since it's undocumented.
#
# In Gtk 2.14 the SpinButton sub-class has the arrow panel as a subwindow
# too (instead of an overlay in Gtk 2.12 and earlier).  So look for the
# smaller height one among multiple subwindows.
#
sub Gtk2::Entry::Gtk2_Ex_WidgetCursor_windows {
  my ($widget) = @_;
  my $win = $widget->window || return; # if unrealized
  return ($win, $win->get_children);
}
sub Gtk2::Entry::Gtk2_Ex_WidgetCursor_hack_restore {
  my ($widget) = @_;
  $widget->sensitive or return;
  my $win = $widget->window || return; # if unrealized
  my @children = $win->get_children;
  # by increasing height
  @children = sort {($a->get_size)[1] <=> ($b->get_size)[1]} @children;
  return ($children[0], 'xterm');
}
# GtkSpinButton's extra "panel" overlay window either as a "sibling" (which
# also finds the main window) for Gtk 2.12 or in the get_children() for Gtk
# 2.13; plus the GtkEntry subwindow as per GtkEntry above.  hack_restore()
# inherited from GtkEntry above.
#
sub Gtk2::SpinButton::Gtk2_Ex_WidgetCursor_windows {
  my ($widget) = @_;
  my $win = $widget->window || return; # if unrealized
  return (_widget_sibling_windows ($widget),
          $win->get_children);
}

# GtkButton secret input-only "event_window" overlay found as a "sibling".
#
sub Gtk2::Button::Gtk2_Ex_WidgetCursor_windows {
  my ($widget) = @_;
  return _widget_sibling_windows ($widget);
}

# _widget_sibling_windows() returns a list of the "sibling" windows of
# $widget.  This means all the windows which are under $widget's parent and
# have their events directed to $widget.  If $widget is a windowed widget
# then this will include its main $widget->window (or should do).
#
# The search works by seeing where a dummy expose event is directed by
# gtk_get_event_widget().  It'd also be possible to inspect
# gdk_window_get_user_data(), but Gtk2-Perl only returns an "unsigned" for
# that so it'd need some nasty digging for the widget address.
#
# In the past the code here cached the result against the widget (what was
# then just GtkButton's "event_window" sibling), with weakening of course so
# unrealize would destroy the windows as normal.  But don't bother with that
# now, on the basis that cursor changes hopefully aren't so frequent as to
# need too much trouble, and that it's less prone to mistakes if not cached
# :-).
#
sub _widget_sibling_windows {
  my ($widget) = @_;
  my $parent_win = ($widget->flags & 'no-window'
                    ? $widget->window
                    : $widget->get_parent_window)
    || return; # if unrealized

  my $event = Gtk2::Gdk::Event->new ('expose');
  return grep { $event->window ($_);
                ($widget == (Gtk2->get_event_widget($event) || 0))
              } $parent_win->get_children;
}

# Return true if $widget is the Gtk2::EventBox child of a Gtk2::Combo popup
# window (it's a child of the popup window, not of the Combo itself).
#
sub _widget_is_combo_eventbox {
  my ($widget) = @_;
  my $parent;
  return ($widget->isa('Gtk2::EventBox')
          && ($parent = $widget->get_parent)  # might not have a parent
          && $parent->get_name eq 'gtk-combo-popup-window');
}


#------------------------------------------------------------------------------

# Could think about documenting this idle level to the world, maybe like the
# following, but would it be any use?
#
# =item C<$Gtk2::Ex::WidgetCursor::busy_idle_priority>
#
# The priority level of the (C<< Glib::Idle->add >>) handler installed by
# C<busy>.  This is C<G_PRIORITY_DEFAULT_IDLE - 10> by default, which is
# designed to stay busy through Gtk resizing and redrawing at around
# C<G_PRIORITY_HIGH_IDLE>, but end the busy before ordinary "default idle"
# tasks.



( run in 1.394 second using v1.01-cache-2.11-cpan-39bf76dae61 )