On Dec 30, 2005, at 11:07 AM, Jörn Reder wrote:
I like to have a child window on my application window which has a
fixed
position relative to the button which opens it (somewhat like a Popup
menu). It should look like this:
+-------------------------------------------------+
| Main App Window |
| |
| +----------------------------------+
| | Child Window |
| | |
| +-------------------------+--------+
| | Button |
+----------------------------------------+--------+
The child window should overlap other widgets in the main window
and can
be closed again on user action. If the main window is resized, the
child
window should keep attached at the right side and above the button.
What's the easiest way to accomplish that?
The most obvious option is to create a GTK_WINDOW_POPUP window with
GDK_GRAVITY_SOUTH_EAST, float it above the main window, and use
gtk_window_move() to track its location in the main window's
configure-event. However, this doesn't really work out that well.
My window manager refused to honor set_transient_for() for the popup
window, so i had to call $popup->window->raise every time i relocated
it. This resulted in some unpleasant flashing and move lagging.
A slightly sneakier solution is to use an actual widget as a fake
popup, by using manual absolute position layout. We just tell the
widget that its parent is the toplevel window, and then update the
widget's position from the toplevel window's size-allocate as though
we were implementing a fixed-position layout container. The
advantage here is that the "popup" tracks the toplevel's position
perfectly during moves because the X server moves the GdkWindow for
us, removing all of the lag and z-order problems of the separate
window solution. Also, using size-allocate is a little easier. ;-)
In both solutions, the keyboard focus is an interesting issue. The
code i implemented ignores the "popup"'s keyboard focus altogether --
it doesn't get focus when toggled visible, and doesn't get inserted
into the toplevel's focus chain. You didn't mention whether you
wanted to have actual interactive widgets in your popup, or whether
it was just for information display. I leave that as an exercise for
you. ;-)
Attached is a 100-line prototype that implements the popup in a quick
and dirty procedural style.
#!/usr/bin/perl -w
use strict;
use Gtk2 -init;
use Glib qw(:constants);
{
# main window
my $window = Gtk2::Window->new;
$window->signal_connect (destroy => sub {Gtk2->main_quit});
$window->set_default_size (400, 300);
my $vbox = Gtk2::VBox->new;
my $hbox = Gtk2::HBox->new;
my $scroller = Gtk2::ScrolledWindow->new;
my $textview = Gtk2::TextView->new;
my $toggle = Gtk2::ToggleButton->new ('Poppy');
$window->add ($vbox);
$vbox->add ($scroller);
$scroller->add ($textview);
$vbox->pack_start ($hbox, FALSE, FALSE, 0);
$hbox->pack_end ($toggle, FALSE, FALSE, 0);
# Toggle the visibility of the "popup" whenever $toggle is toggled.
$toggle->signal_connect (toggled => \&toggle_popup);
# We need to keep a reference to this so we can use it as the relative
# base for the popup's location.
$window->{popup_toggle} = $toggle;
# Since we're managing the popup ourselves, we'll have to relocate
# it whenever the main window's size changes. We don't have to
# worry about tracking movement of the main window, because we're
# going to make the "popup" be a child widget, rather than a proper
# popup window.
$window->signal_connect (size_allocate => \&allocate_main_window);
$window->show_all;
Gtk2->main;
}
sub toggle_popup {
my $toggle = shift;
my $window = $toggle->get_toplevel;
my $popup = $window->{popup};
if (!$popup) {
# Create a little frame with some interesting stuff
# inside, and place it in an event box that will
# serve as the popup.
$popup = Gtk2::EventBox->new;
my $frame = Gtk2::Frame->new ("Cool shit");
my $vbox = Gtk2::VBox->new;
my $thing1 = Gtk2::CheckButton->new ("thing 1");
my $thing2 = Gtk2::CheckButton->new ("thing 2");
$vbox->add ($thing1);
$vbox->add ($thing2);
$frame->add ($vbox);
$popup->add ($frame);
$frame->show_all;
# Tell the frame that it belongs to the window.
# We'll manage its position in update_popup_position.
$popup->set_parent ($window);
$window->{popup} = $popup; # for later.
}
if ($toggle->get_active) {
update_popup_position ($window);
$popup->show;
} else {
$popup->hide;
}
}
sub update_popup_position {
my ($window) = @_;
my $popup = $window->{popup};
return unless $popup;
# Keep the popup's lower-right corner pinned to the upper-right
# corner of the popup toggle. We're acting as though we are
# the popup's container, so use size_allocate() to tell the
# widget where to live. Note that we do this regardless of
# whether the widget is visible.
my $toggle_allocation = $window->{popup_toggle}->allocation;
my $popup_width = $popup->size_request->width;
my $popup_height = $popup->size_request->height;
my $allocation = Gtk2::Gdk::Rectangle->new (
$toggle_allocation->x + $toggle_allocation->width
- $popup_width,
$toggle_allocation->y - $popup_height,
$popup_width,
$popup_height);
$popup->size_allocate ($allocation);
}
sub allocate_main_window {
my ($window) = @_;
# The size of the main window has changed; position the
# popup accordingly.
update_popup_position ($window);
return FALSE;
}
--
Our enemies are innovative and resourceful, and so are we. They never
stop thinking about new ways to harm our country and our people, and
neither do we.
-- President George W. Bush
_______________________________________________
gtk-perl-list mailing list
[email protected]
http://mail.gnome.org/mailman/listinfo/gtk-perl-list