[Top] [Prev] [Next] [Bottom]

22

22 The Place Geometry Manager

This chapter explores the place geometry manager that positions widgets on the screen.
The place geometry manager is much simpler than pack and grid. You specify the exact position and size of a window, or you specify the relative position and relative size of a widget. This is useful in a few situations, but it rapidly becomes tedious if you have to position lots of windows. The best application of place is to create special-purpose geometry managers using its relative constraints. A standard application of place is to adjust the boundary between two adjacent windows.

place Basics

The place command lets you specify the width and height of a window, and the X and Y locations of the window's anchor point. The size and location can be specified in absolute or relative terms. Relative specifications are more powerful. Example 22-1 uses place to center a window in its parent. You can use this command to position dialogs that you do not want to be detached top-level windows:

Centering a window with place.
place $w -in $parent -relx 0.5 -rely 0.5 -anchor center
The -relx and -rely specify the relative X and Y positions of the anchor point of the widget $w in $parent. A relative X (or Y) value of zero corresponds to the left (or top) edge of $parent. A value of one corresponds to the right (or bottom) edge of $parent. A value of 0.5 specifies the middle. The anchor point determines what point in $w is positioned according to the specifications. In Example 22-1 the center anchor point is used so that the center of $w is centered in $parent.

The relative height and width settings are used to base a widget's size on another widget. Example 22-2 completely covers one window with another window. It uses the default anchor point for windows, which is their upper-left hand corner (nw):

Covering a window with place.
place $w -in $parent -relwidth 1 -relheight 1 -x 0 -y 0
The absolute and relative size and position parameters are additive (e.g., -width and -relwidth). You can make a window slightly larger or smaller than the parent by specifying both parameters. In Example 22-3 a negative width and height are used to make a window smaller than another one:

Combining relative and absolute sizes.
place $w -in $parent -relwidth 1 -relheight 1 -x 0 -y 0 \
	-width -4 -height -4
It is not necessary for $parent to actually be the parent widget of $w. The requirement is that $parent be the parent, or a descendent of the parent, of $w. It also has to be in the same top-level window. This guarantees that $w is visible whenever $parent is visible. These are the same restrictions imposed by the pack geometry manager.

It is not necessary to position a widget inside another widget, either. Example 22-4 positions a window five pixels above a sibling widget. If $sibling is repositioned, then $w moves with it. This approach is useful when you decorate a resizable window by placing other widgets at its corners or edges. When the window is resized, the decorations automatically move into place:

Positioning a window above a sibling with place.
place $w -in $sibling -relx 0.5 -y -5 -anchor s \
	-bordermode outside
The -bordermode outside option is specified so that any decorative border in $sibling is ignored when positioning $w. In this case the position is relative to the outside edge of $sibling. By default, the border is taken into account to make it easy to position widgets inside their parent's border.

The parent widget does not have to be a frame. Example 22-1 can be used to place a dialog in the middle of a text widget. In Example 22-4, $sibling and $w can both be label widgets.

The Pane Manager

The relative size and placement parameters of the place command can be used to create custom geometry managers. Example 22-5 shows a paned layout manager. Two frames, or panes, are placed inside another frame. A small third frame represents a grip that is used to adjust the boundary between the two panes:

Pane_Create sets up vertical or horizontal panes.
proc Pane_Create {f1 f2 args} {

	# Map optional arguments into array values
	set t(-orient) vertical
	set t(-percent) 0.5
	set t(-in) [winfo parent $f1]
	array set t $args

	# Keep state in an array associated with the master frame
	set master $t(-in)
	upvar #0 Pane$master pane
	array set pane [array get t]

	# Create the grip and set placement attributes that
	# will not change. A thin divider line is achieved by
	# making the two frames one pixel smaller in the
	# adjustable dimension and making the main frame black.

	set pane(1) $f1
	set pane(2) $f2
	set pane(grip) [frame $master.grip -background gray50 \
		-width 10 -height 10 -bd 1 -relief raised \
		-cursor crosshair]
	if {[string match vert* $pane(-orient)]} {
		set pane(D) Y		;# Adjust boundary in Y direction
		place $pane(1) -in $master -x 0 -rely 0.0 -anchor nw \
			-relwidth 1.0 -height -1
		place $pane(2) -in $master -x 0 -rely 1.0 -anchor sw \
			-relwidth 1.0 -height -1
		place $pane(grip) -in $master -anchor c -relx 0.8
	} else {
		set pane(D) X 		;# Adjust boundary in X direction
		place $pane(1) -in $master -relx 0.0 -y 0 -anchor nw \
			-relheight 1.0 -width -1
		place $pane(2) -in $master -relx 1.0 -y 0 -anchor ne \
			-relheight 1.0 -width -1
		place $pane(grip) -in $master -anchor c -rely 0.8
	}
	$master configure -background black

	# Set up bindings for resize, <Configure>, and 
	# for dragging the grip.

	bind $master <Configure> [list PaneGeometry $master]
	bind $pane(grip) <ButtonPress-1> \
		[list PaneDrag $master %$pane(D)]
	bind $pane(grip) <B1-Motion> \
		[list PaneDrag $master %$pane(D)]
	bind $pane(grip) <ButtonRelease-1> \
		[list PaneStop $master]

	# Do the initial layout

	PaneGeometry $master
}

Parsing Arguments and Maintaining State

The Pane_Create procedure is given two widgets to manage, and an optional set of parameters. The general syntax of Pane_Create is:

Pane_Create f1 f2 ?-orient xy? ?-percent p? ?-in master?
All the optional arguments are available in $args. Its attribute-value structure is used to initialize a temporary array t. Default values are set before the assignment from $args. The following code is compact but doesn't check errors in the optional arguments.

set t(-orient) vertical

set t(-percent) 0.5
set t(-in) [winfo parent $f1]
array set t $args

Global state about the layout is kept in an array whose name is based on the master frame. The name of the master frame isn't known until after arguments are parsed, which is why t is used. After the upvar the argument values are copied from the temporary array into the global state array:

set master $t(-in)

upvar #0 Pane$master pane
array set pane [array get t]

Sticky Geometry Settings

Example 22-5 sets several place parameters on the frames when they are created. These are remembered, and other parameters are adjusted later to dynamically adjust the boundary between the frames. All Tk geometry managers retain settings like this. The initial settings for the vertical layout is shown here:

place $pane(1) -in $parent -x 0 -rely 0.0 -anchor nw \

	-relwidth 1.0 -height -1	
place $pane(2) -in $parent -x 0 -rely 1.0 -anchor sw \
	-relwidth 1.0 -height -1
place $pane(grip) -in $parent -anchor c -relx 0.8

The position of the upper and lower frames is specified with an absolute X and a relative Y position, and the anchor setting is chosen to keep the frame visible inside the main frame. For example, the lower frame is positioned at the bottom-left corner of the container with -x 0 and -rely 1.0. The -anchor sw attaches the lower-left corner of the frame to this position.

The size of the contained frames is also a combination of absolute and relative values. The width is set to the full width of the container with -relwidth 1.0. The height is set to minus one with -height -1. This value gets added to a relative height that is determined later. It will leave a little space between the two contained frames.

The resize grip is just a small frame positioned at the boundary. Initially it is just placed over toward one size with -relx 0.8. It gets positioned on the boundary with a -rely setting later. It has a different cursor to indicate it is active.

Event Bindings

The example uses some event bindings that are described in more detail in Chapter 23. The <Configure> event occurs when the containing frame is resized by the user. When the user presses the mouse button over the grip and drags it, there is a <ButtonPress-1> event, one or more <B1-Motion> events, and finally a <ButtonRelease-1> event. Tcl commands are bound to these events:

bind $parent <Configure> [list PaneGeometry $parent]

bind $pane(grip) <ButtonPress-1> \
	[list PaneDrag $parent %$pane(D)]
bind $pane(grip) <B1-Motion> \
	[list PaneDrag $parent %$pane(D)]
bind $pane(grip) <ButtonRelease-1> [list PaneStop $parent]

Managing the Layout

The code is set up to work with either horizontal or vertical layouts. The pane(D) variable is either X, for a horizontal layout, or Y, for a vertical layout. This value is used in the bindings to get %X or %Y, which are replaced with the X and Y screen positions of the mouse when the bindings fire. This value is passed to PaneDrag as the parameter D. The PaneDrag procedure remembers the previous position in pane(lastD) and uses that to update the percentage split between the two contained panes:

PaneDrag adjusts the percentage.
proc PaneDrag {master D} {
	upvar #0 Pane$master pane
	if [info exists pane(lastD)] {
		set delta [expr double($pane(lastD) - $D) \
									/ $pane(size)]
		set pane(-percent) [expr $pane(-percent) - $delta]
		if {$pane(-percent) < 0.0} {
			set pane(-percent) 0.0
		} elseif {$pane(-percent) > 1.0} {
			set pane(-percent) 1.0
		}
		PaneGeometry $master
	}
	set pane(lastD) $D
}
proc PaneStop {master} {
	upvar #0 Pane$master pane
	catch {unset pane(lastD)}
}
The PaneGeometry procedure adjusts the positions of the frames. It is called when the main window is resized, so it updates pane(size). It is also called as the user drags the grip. For a vertical layout, the grip is moved by setting its relative Y position. The size of the two contained frames is set with a relative height. Remember this is combined with the fixed height of -1 to get some space between the two frames:

PaneGeometry updates the layout.
proc PaneGeometry {master} {
	upvar #0 Pane$master pane
	if {$pane(D) == "X"} {
		place $pane(1) -relwidth $pane(-percent)
		place $pane(2) -relwidth [expr 1.0 - $pane(-percent)]
		place $pane(grip) -relx $pane(-percent)
		set pane(size) [winfo width $master]
	} else {
		place $pane(1) -relheight $pane(-percent)
		place $pane(2) -relheight [expr 1.0 - $pane(-percent)]
		place $pane(grip) -rely $pane(-percent)
		set pane(size) [winfo height $master]
	}
}
proc PaneTest {{p .p} {orient vert}} {
	catch {destroy $p}
	frame $p -width 200 -height 200
	label $p.1 -bg blue -text foo
	label $p.2 -bg green -text bar
	pack $p -expand true -fill both
	pack propagate $p off
	Pane_Create $p.1 $p.2 -in $p -orient $orient -percent 0.3
}

The place Command

Table 22-1 summarizes the usage of the place command.
The place command.
place win ?win ..? ?options? This is just like place configure.
place configure win ?win ...? ?options? Place one or more widgets according to the options, which are given Table 22-2.
place forget win ?win...? Unmap the specified windows.
place info win Return the placement parameters of win.
place slaves win Return the list of widgets managed by win.

Table 22-2 summarizes the placement options for a widget. These are set with the place configure command, and the current settings are returned by the place info command.
Placement options.
-in win Place inside (or relative to) win.
-anchor where Anchors: center, n, ne, e, se, s, sw, w, or nw. Default: nw.
-x coord X position, in screen units, of the anchor point.
-relx offset Relative X position. 0.0 is the left edge. 1.0 is the right edge.
-y coord Y position, in screen units, of the anchor point.
-rely offset Relative Y position. 0.0 is the top edge. 1.0 is the bottom edge.
-width size Width of the window, in screen units.
-relwidth size Width relative to parent's width. 1.0 is full width.
-height size Height of the window, in screen units.
-relheight size Height relative to the parent's height. 1.0 is full height.
-bordermode mode If mode is inside, then size and position are inside the parent's border. If mode is outside, then size and position are relative to the outer edge of the parent. The default is inside.



[Top] [Prev] [Next] [Bottom]

[email protected]
Copyright © 1997, Brent Welch. All rights reserved.
This will be published by Prentice Hall as the 2nd Edition of
Practical Programming in Tcl and Tk