<!--
  - Authors:
  -   Christian Schulte <schulte@ps.uni-sb.de>
  -
  - Contributors:
  -   Daniel Simon <dansim@ps.uni-sb.de>
  -   Andy Walter <anwalt@ps.uni-sb.de>
  -   
  - Copyright:
  -   Christian Schulte, 1998
  -
  - Last change:
  -   $Date: 1999-08-19 15:02:09 +0200 (Thu, 19 Aug 1999) $ by $Author: schulte $
  -   $Revision: 11856 $
  -
  - This file is part of Mozart, an implementation
  - of Oz 3
  -    http://www.mozart-oz.org
  -
  - See the file "LICENSE" or
  -    http://www.mozart-oz.org/LICENSE.html
  - for information on usage and redistribution
  - of this file, and for a DISCLAIMER OF ALL
  - WARRANTIES.
  -
  -->

<Chapter id="chapter.geometry">
  <Title/Geometry Managers/

  <P>
    This chapter explains geometry managers.  Geometry managers compute how
    much space widgets occupy and at which position they appear on the screen.
    Last but not least they make widgets appear on the screen with the
    geometry computed previously.

  <Section id="section.geometry.intro">
    <Title/Widgets and Parcels/

    <P>
      A geometry manager computes the size and location of widgets, that is
      the geometry, and displays the widgets on the screen.  The geometry
      manager computes the geometry according to the widget hierarchy.
      During computation of the geometry, the manager takes the following
      three things into account:
      <List enum>
	<Item>
	  The geometry requested by slave widgets.  Widgets like labels and
	  messages request just enough space to displays their text or bitmap.
	<Item>
	  The geometry requested by master widgets.  Usually master widgets
	  do not request an explicit geometry.  But for example, if a frame
	  widget is initialized with explicit values for width and height, the
	  geometry manager takes these values into account.
	<Item>
	  The options given to the geometry manager.
      </List>

    <para class=apropos><title/parcels/
      The geometry manager computes for each slave of a master widget a
      so-called <Def/parcel/.  The parcel is a rectangle and describes the
      space and the position computed for the slave.  From the slaves'
      parcels the manager computes the parcel for the master.  If the master
      does not request a specific geometry on its own, the manager will
      assign the master a parcel that encloses all slave parcels.  Otherwise
      the geometry manager distributes the space in the parcel for the
      master to the slave parcels. This may shrink or grow the parcels for
      the slaves.

    <P>
      Options to the geometry manager affect usually the way how parcels are
      computed and how widgets are put into their parcels, if the parcels
      are larger (or smaller) than the parcel initially requested by the
      widget.

    <P>
      <index/packer/<index/pack/<index/grid/In the following we will show the two most important geometry managers
      which are provided in Tk.  One is the packer, which can be used for
      simple arrangements, like placing several widgets in a row or in a
      line.  The other geometry manager we will discuss is implemented by
      the <<grid>> command.  As the name suggests, the grid command allows
      for arranging widgets in a grid-like fashion. 

    <P>
      It is perfectly possible to mix geometry managers in a single toplevel
      widget provided that all slaves of a master are managed by the same
      manager.  For example, suppose a toplevel widget that contains two
      frames which contain widgets themselves.  Both frames must be managed
      by the same manager.  The widgets in the frames can be managed by two
      different managers.

    <P>
      We discuss only the most important options these two managers provide,
      a complete description can be found in <ref.extern
      to="&tkcmd/pack.htm"/<kbd/pack// and <ref.extern
      to="&tkcmd/grid.htm"/<kbd/grid//. More information on the packer
      can also be found in <ref to="Ousterhout.94"/John Ousterhout's
      book/ in Chapter&nbsp;17.

  <Section id="section.geometry.pack">
    <Title/The Packer/

    <P>
      The packer supports simple arrangements of widgets in rows and
      columns.  Arranging widgets nicely usually also means that some
      vertical and horizontal space has to be inserted, either designed to
      provide for additional space or to fill up space not occupied by the
      widget's original size.

    <P>
      The different ways how to affect the geometry we will study by means
      of examples.  For this, let us assume we are dealing with three label
      widgets.  The following function creates a toplevel widget with
      background color white for better visibility, and returns a list of
      three labels.
<chunk class=anonymous><title/NewLabels/
fun {NewLabels}
   W={New Tk.toplevel tkInit(background:white)}
in
   {Map ['label' 'Second label widget' '3rd label']
    fun {$ A}
       {New Tk.label tkInit(parent:W text:A)}
    end}
end
</chunk>

    <P>
      To display the labels in the toplevel widget, the packer can be
      invoked as follows:
<index/<<pack>>/
<chunk class=anonymous><title/pack basic/
[L1 L2 L3] = {NewLabels}
{Tk.send pack(L1 L2 L3)}
</chunk>

    <P id="para.geometry.batch">
      This computes and displays a geometry for the toplevel widget as shown
      in <Ptr to="figure.geometry.pack-basic">.  Rather than giving a tickle
      which contains each of the labels as field we can give a <Def/batch
      tickle/.  A batch tickle is a tuple with label&nbsp;<<b>> where its
      single argument must be a list of tickles.  By using a batch tickle,
      we can rewrite our example from above to
<chunk class=anonymous><title/pack batch/
{Tk.send pack(b({NewLabels}))}
</chunk>
      where the list of tickles is the list of labels as returned by the
      function <<NewLabels>>.
    <p>
      <Figure id="figure.geometry.pack-basic">
	<Caption/Plain geometry computed by the packer./
               <picture.choice>
	<Picture.Extern to="packer-plain.gif" type=gif>
	<Picture.Extern id="pict.ext.geometry.packer-plain" to="pictures/geometry/packer-plain.eps" type=ps>
               </picture.choice>
      </Figure>
     

    <SubSection>
      <Title/Side Options/

      <P>
        <index class=tails/option<and><<side>>/
        <index class=tails/option<and><<side>><and><<left>>/
        <index class=tails/option<and><<side>><and><<right>>/
        <index class=tails/option<and><<side>><and><<top>>/
        <index class=tails/option<and><<side>><and><<bottom>>/
	The label widgets in the previous examples were placed from the top to
	the bottom of the toplevel widget.  The side where the widgets are
	packed against can be determined with the <<side>> option.  The default
	value for this option is <<top>>.  The examples in <Ptr
	to="figure.geometry.packer-side"> show the geometry which is
	computed when <<left>> and <<bottom>> are given as values for the
	side option.  Valid values for the side option are <<top>>, <<bottom>>,
	<<left>>, and <<right>>.

      <p>
	<Figure id="figure.geometry.packer-side">
	  <Caption
	    /Geometries computed by the packer according to <<side>> option./
	  <P>
               <picture.choice>
	    <Picture.Extern to="packer-left.gif" type=gif>
	    <Picture.Extern id="pict.ext.geometry.packer-left" to="pictures/geometry/packer-left.eps" type=ps>
               </picture.choice>
<chunk class=anonymous><title/pack left/
{Tk.send pack(b({NewLabels}) side:left)}
</chunk>
               <picture.choice>
	    <Picture.Extern to="packer-bottom.gif" type=gif>
	    <Picture.Extern id="pict.ext.geometry.packer-bottom" to="pictures/geometry/packer-bottom.eps" type=ps>
               </picture.choice>
<chunk class=anonymous><title/pack bottom/
{Tk.send pack(b({NewLabels}) side:bottom)}
</chunk>
	</Figure>

    <SubSection>
      <Title/Padding/

      <P>
        <index/padding/
        <index class=tails/option<and><<padx>>/
        <index class=tails/option<and><<pady>>/
        <index class=tails/option<and><<ipadx>>/
        <index class=tails/option<and><<ipady>>/
	The geometry computed for widgets by the packer can be given
	additional space in two different ways: either externally or
	internally.  Additional external space can be specified with the
	options <<padx>> and <<pady>>.  The values for these options must be
	valid screen distances (see <Ptr to="section.widgets-1.distances">),
	specifying how much additional space should be provided by the master
	widget around the packed widgets.  The internal space can be specified
	by the <<ipadx>> and <<ipady>> options, where the values must be screen
	distances as well.  These values determine by how much space the packed
	widgets are expanded in each of their four borders.  The examples in
	<Ptr to="figure.geometry.packer-pad"> show the effects on the
	geometries computed by the packer for both internal and external
	padding.

	<Figure id="figure.geometry.packer-pad">
	  <Caption/Additional space provided by the packer./
	  <P>
               <picture.choice>
	    <Picture.Extern to="packer-pad.gif" type=gif>
	    <Picture.Extern id="pict.ext.geometry.packer-pad" to="pictures/geometry/packer-pad.eps" type=ps>
               </picture.choice>
<chunk class=anonymous><title/pack pad/
{Tk.send pack(b({NewLabels}) padx:1#m pady:1#m)}
</chunk>
               <picture.choice>
	    <Picture.Extern to="packer-ipad.gif" type=gif>
	    <Picture.Extern id="pict.ext.geometry.packer-ipad" to="pictures/geometry/packer-ipad.eps" type=ps>
               </picture.choice>
<chunk class=anonymous><title/pack ipad/
{Tk.send pack(b({NewLabels}) ipadx:2#m ipady:2#m)}
</chunk>
	</Figure>

    <SubSection id="section.geometry.anchors">
      <Title/Anchors/

      <P>
        <index/anchor/
        <index class=tails/option<and><<anchor>>/
	With the <<anchor>> option it can be specified where in a widget's
	parcel the packer places the widget. If no <<anchor>> option is given,
	the packer places the widget in the center of its parcel. Otherwise,
	the widget is placed according to the option's value, which can be one
	of <<center>>, <<n>>, <<s>>, <<w>>, <<e>>, <<nw>>, <<ne>>, <<sw>>,
	and <<se>>.  The <Ptr to="figure.geometry.packer-anchor"> shows the
	geometry computed when <<w>> is used as anchor.

	<Figure id="figure.geometry.packer-anchor">
	  <Caption/Using the <<anchor>> option for packing./
	  <P>
               <picture.choice>
	    <Picture.Extern to="packer-anchor.gif" type=gif>
	    <Picture.Extern id="pict.ext.geometry.packer-anchor" to="pictures/geometry/packer-anchor.eps" type=ps>
               </picture.choice>
<chunk class=anonymous><title/pack anchor/
{Tk.send pack(b({NewLabels}) anchor:w padx:1#m pady:1#m)}
</chunk>
	</Figure>
      <P>

    <SubSection>
      <Title/Filling and Expansion/

      <P>
	For pleasant overall geometry it is imported that widgets have similar
	geometries.  The packer employs two different schemes how widgets can
	be arranged to have similar geometries.  One is filling: the widget
	extends over its entire parcel.  The other one is expansion: the
	widget's parcel is extended such that the parcels of all slaves in
	a master occupy the master's parcel entirely.

      <P>
        <index class=tails/option<and><<fill>>/
        <index class=tails/option<and><<fill>><and><<x>>/
        <index class=tails/option<and><<fill>><and><<y>>/
        <index class=tails/option<and><<fill>><and><<both>>/
        <index class=tails/option<and><<fill>><and><<none>>/
	<Ptr to="figure.geometry.packer-fill"> shows the geometry computed when
	the option <<fill>> with value&nbsp;<<x>> is used. Possible values for
	the <<fill>> option are <<x>>, <<y>>, <<both>>, and <<none>> (which is
	the default).

	<Figure id="figure.geometry.packer-fill">
	  <Caption/Using the <<fill>> option for packing./
	  <P>
               <picture.choice>
	    <Picture.Extern to="packer-fill.gif" type=gif>
	    <Picture.Extern id="pict.ext.geometry.packer-fill" to="pictures/geometry/packer-fill.eps" type=ps>
               </picture.choice>
<chunk class=anonymous><title/pack fill/
{Tk.send pack(b({NewLabels}) fill:x)}
</chunk>
	</Figure>

      <P>
        <index class=tails/option<and><<expand>>/
	Expansion is only significant when the parcels of the slave do not
	fill the master's parcel completely.  In all our previous examples,
	the parcel of the master was computed by the packer to be just large
	enough to contain the slave's parcels.  So there was no additional
	space in the master's parcel to be filled by expansion of slave
	parcels. 

      <P>
	<Ptr to="figure.geometry.packer-resize"> shows three toplevel widgets
	which have been resized manually by dragging with the mouse.  The top
	most example shows that when the parcel of the toplevel widget grows,
	the remaining space is filled by the label widgets.  In the example
	in the middle, only the parcels of the label widget's are expanded.
	At the bottom, the parcels are expanded and then filled up in both
	horizontal and vertical direction by the label widgets.

	<Figure id="figure.geometry.packer-resize">
	  <Caption/Resizing effects for filling and expansion./
	  <P>
               <picture.choice>
	    <Picture.Extern to="packer-resize-fill.gif" type=gif>
	    <Picture.Extern id="pict.ext.geometry.packer-resize-fill" to="pictures/geometry/packer-resize-fill.eps" type=ps>
               </picture.choice>
<chunk class=anonymous><title/pack fill expand/
{Tk.send pack(b({NewLabels}) fill:x)}
</chunk>
               <picture.choice>
	    <Picture.Extern to="packer-resize-expand.gif" type=gif>
	    <Picture.Extern id="pict.ext.geometry.packer-resize-expand" to="pictures/geometry/packer-resize-expand.eps" type=ps>
               </picture.choice>
<chunk class=anonymous><title/pack expand expand/
{Tk.send pack(b({NewLabels}) expand:true)}
</chunk>
               <picture.choice>
	    <Picture.Extern to="packer-resize-both.gif" type=gif>
	    <Picture.Extern id="pict.ext.geometry.packer-resize-both" to="pictures/geometry/packer-resize-both.eps" type=ps>
               </picture.choice>
<chunk class=anonymous><title/pack both expand/
{Tk.send pack(b({NewLabels}) fill:both expand:true)}
</chunk>
	</Figure>

  <Section id="section.geometry.grid">
    <Title/The Grid Geometry Manager/

    <P>
        <index/grid/
      The grid geometry arranges widgets in a grid-like fashion.  For each
      widget to be managed by the <<grid>> command, a row and a column
      number is given.  The manager computes parcels for the widgets such
      that all parcels in the same column have the same width and all
      parcels in the same row have the same height.

    <P>
      <Ptr to="figure.geometry.grid"> shows how eight labels are placed by
      the <<grid>> command.  Note that it is not necessary that all positions
      in the grid are occupied by a widget.  In our example in <Ptr
      to="figure.geometry.grid">, the position at row and column&nbsp;<<2>>
      does not contain a widget.

      <Figure id="figure.geometry.grid">
	<Caption/Using the <<grid>> command./
	<P>
               <picture.choice>
	  <Picture.Extern to="grid.gif" type=gif>
	  <Picture.Extern id="pict.ext.geometry.grid" to="pictures/geometry/grid.eps" type=ps>
               </picture.choice>
<chunk class=anonymous><title/grid basic/
proc {GL W R C S}
   L={New Tk.label tkInit(parent:W text:S)}
in
   {Tk.send grid(L row:R column:C padx:4 pady:4)}
end
{GL W 1 1 nw}   {GL W 1 2 north} {GL W 1 3 ne}
{GL W 2 1 west}                  {GL W 2 3 east}
{GL W 3 1 sw}   {GL W 3 2 south} {GL W 3 3 sw}
</chunk>
      </Figure>

    <SubSection>
      <Title/Padding/

      <P>
        <index/padding/
        <index class=tails/option<and><<padx>>/
        <index class=tails/option<and><<pady>>/
        <index class=tails/option<and><<ipadx>>/
        <index class=tails/option<and><<ipady>>/
	The <<grid>> command supports padding in the same way as the packer
	does.  In the above example we used external padding by giving <<padx>>
	and <<pady>> options.  It is also possible to use internal padding with
	the options <<ipadx>> and <<ipady>>.

    <SubSection>
      <Title/Span Options/

      <P>
        <index class=tails/option<and><<columnspan>>/
        <index class=tails/option<and><<rowspan>>/
	The grid command can also compute geometries where widgets occupy
	more than a single row or column. In the example shown in <Ptr
	to="figure.geometry.span"> the label widget&nbsp;<<L>> is managed
	by the <<grid>> command to occupy both two rows and two columns.
	How much rows and columns a widget's parcel spans is specified with
	the <<columnspan>> and <<rowspan>> options.

	<Figure id="figure.geometry.span">
	  <Caption/Using the <<columnspan>> and <<rowspan>> options./
	  <P>
               <picture.choice>
	    <Picture.Extern to="grid-span.gif" type=gif>
	    <Picture.Extern id="pict.ext.geometry.grid-span" to="pictures/geometry/grid-span.eps" type=ps>
               </picture.choice>
<chunk class=anonymous><title/grid span/
{Tk.send grid({New Tk.label tkInit(parent:W text:'Upper left')} 
	      row:1    rowspan:2
	      column:1 columnspan:2
	      padx:4 pady:4)}
{GL W 1 3 ne} {GL W 2 3 east}
{GL W 3 1 sw} {GL W 3 2 south} {GL W 3 3 sw}
</chunk>
	</Figure>
      <P>

    <SubSection id="section.geometry.sticky">
      <Title/Sticky Options/

      <P>
        <index class=tails/option<and><<sticky>>/
	The <<grid>> command combines the <<anchor>> and <<fill>> options from
	the packer in a single <<sticky>> option.  The value given for a sticky
	option determines both the side the widget is placed in its parcel,
	and how the widget is to be stretched to fill its parcel.

      <P>
	Valid values for the <<sticky>> option are all combinations of the
	letters <<n>>, <<s>>, <<w>>, and&nbsp;<<e>> in any order.  Giving one
	of <<n>> and&nbsp;<<s>> (or of <<w>> and&nbsp;<<e>>) specifies the
	anchor position of a widget.  Giving both <<n>> and&nbsp;<<s>> (or
	both <<w>> and&nbsp;<<e>>) requests that the widget should fill
	its parcel horizontally (or vertically).  For an example see <Ptr
	to="figure.geometry.sticky">.

	<Figure id="figure.geometry.sticky">
	  <Caption/Using the <<sticky>> option with the <<grid>> command./
	  <P>
               <picture.choice>
	    <Picture.Extern to="grid-sticky.gif" type=gif>
	    <Picture.Extern id="pict.ext.geometry.grid-sticky" to="pictures/geometry/grid-sticky.eps" type=ps>
               </picture.choice>
<chunk class=anonymous><title/grid sticky/
{Tk.send grid({New Tk.label tkInit(parent:W text:'Upper left')} 
	      row:1    rowspan:2
	      column:1 columnspan:2
	      sticky: nse
	      padx:4 pady:4)}
</chunk>
	</Figure>

    <SubSection>
      <Title/Weight Options/

      <P>
        <index class=tails/option<and><<weight>>/
	The grid geometry manager employs a different scheme for expansion of
	parcels than the packer.  Rows and columns in the grid can be assigned
	an integer weight.  Additional space available in the master of the
	grid is distributed between the rows and columns according to their
	relative weight.

      <P>
	For example, if we take the last example and want that all additional
	space is given to the third row and third column, we can do this by
<chunk class=anonymous><title/grid weight/
{Tk.batch [grid(rowconfigure    W 3 weight:1)
           grid(columnconfigure W 3 weight:1)]}
</chunk>
	<Ptr to="figure.geometry.weight"> shows the result of resizing the
	window.

	<Figure id="figure.geometry.weight">
	  <Caption/Result of resizing a window./
               <picture.choice>
	  <Picture.Extern to="grid-weight.gif" type=gif>
	  <Picture.Extern id="pict.ext.geometry.grid-weight" to="pictures/geometry/grid-weight.eps" type=ps>
               </picture.choice>
	</Figure>

  <Section id="section.geometry.anchor">
    <Title/Using Anchors for Widgets/

    <P>
        <index class=tails/option<and><<anchor>>/
      The <<anchor>> option for the packer and the <<sticky>> option for the
      grid geometry manager determine where the widget is placed in its
      parcel.  In the same way several kind of widgets, &eg;, message and
      label widgets, take an anchor <<option>>, which determines where the
      displayed item, &eg;, the text or bitmap, is placed within the widget.

    <P>
      <Ptr to="figure.geometry.widget-anchor"> shows an example for the three
      label widgets used throughout <Ptr to="section.geometry.pack">.  The
      possible values for the anchor options are the same as described in
      <Ptr to="section.geometry.anchors">.

      <Figure id="figure.geometry.widget-anchor">
	<Caption/Widgets with <<anchor>> options./
	<P>
               <picture.choice>
	  <Picture.Extern to="widget-anchor.gif" type=gif>
	  <Picture.Extern id="pict.ext.geometry.widget-anchor" to="pictures/geometry/widget-anchor.eps" type=ps>
               </picture.choice>
<chunk class=anonymous><title/widget anchor/
[L1 L2 L3]={NewLabels}
{Tk.send pack(L1 L2 L3 fill:x)}
{L1 tk(configure anchor:w)} 
{L3 tk(configure anchor:e)}
</chunk>
      </Figure>


<![ %EXTRA; [
<section><title/Examples/
<p>
<chunk><title/EXAMPLES/
%%%
%%% Chapter: Geometry Managers
%%%

%%
%% The Packer
%%

declare
<chunk.ref/NewLabels/

declare
<chunk.ref/pack basic/

<chunk.ref/pack batch/


%% Side Options
<chunk.ref/pack left/

<chunk.ref/pack bottom/


%% Padding
<chunk.ref/pack pad/

<chunk.ref/pack ipad/


%% Anchors
<chunk.ref/pack anchor/


%% Filling and Expansion
<chunk.ref/pack fill expand/

<chunk.ref/pack expand expand/

<chunk.ref/pack both expand/



%%
%% The Grid Geometry Manager
%%

declare
W={New Tk.toplevel tkInit(bg:white)}
<chunk.ref/grid basic/


%% Span Options
declare
W={New Tk.toplevel tkInit(bg:white)}
<chunk.ref/grid span/


%% Sticky Options
declare
W={New Tk.toplevel tkInit(bg:white)}
<chunk.ref/grid sticky/
{GL W 1 3 ne} {GL W 2 3 east}
{GL W 3 1 sw} {GL W 3 2 south} {GL W 3 3 sw}


%% Weight Options
<chunk.ref/grid weight/



%%
%% Using Anchors for Widgets
%%

declare
<chunk.ref/widget anchor/





</chunk>
]]>

</Chapter>
