VerticalPanelView.st
author claus
Fri, 12 May 1995 20:25:18 +0200
changeset 127 462396b08e30
parent 125 3ffa271732f7
child 130 338e856bddc9
permissions -rw-r--r--
.

"
 COPYRIGHT (c) 1989 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

'From Smalltalk/X, Version:2.10.5 on 9-may-1995 at 12:07:08 pm'!

PanelView subclass:#VerticalPanelView
	 instanceVariableNames:''
	 classVariableNames:''
	 poolDictionaries:''
	 category:'Views-Layout'
!

VerticalPanelView comment:'
COPYRIGHT (c) 1989 by Claus Gittinger
	      All Rights Reserved

$Header: /cvs/stx/stx/libwidg/VerticalPanelView.st,v 1.10 1995-05-12 18:25:18 claus Exp $
'!

!VerticalPanelView class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1989 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

version
"
$Header: /cvs/stx/stx/libwidg/VerticalPanelView.st,v 1.10 1995-05-12 18:25:18 claus Exp $
"
!

documentation
"
    a View which arranges its child-views in a vertical column.
    All real work is done in PanelView - except the layout computation is
    redefined here.

    The layout is controlled by the instance variables: 
	horizontalLayout and verticalLayout
    in addition to 
	horizontalSpace and verticalSpace.

    The vertical layout can be any of:

	#top            arrange elements at the top
	#topSpace       arrange elements at the top, start with spacing
	#bottom         arrange elements at the bottom
	#bottomSpace    arrange elements at the bottom, start with spacing
	#center         arrange elements in the center; ignore verticalSpace
	#spread         spread elements evenly; ignore verticalSpace
	#spreadSpace    spread elements evenly with spacing at ends; ignore verticalSpace
	#fit            like spread, but resize elements for tight packing; ignore verticalSpace
	#fitSpace       like fit, with spacing; ignore verticalSpace

    the horizontal layout can be:

	#left           place element at the left
	#leftSpace      place element at the left, offset by horizontalSpace
	#center         place elements horizontally centered; ignore horizontalSpace
	#right          place it at the right
	#rightSpace     place it at the right, offset by horizontalSpace
	#fit            resize elements horizontally to fit this panel; ignore horizontalSpace
	#fitSpace       like fit, but add spacing; ignore horizontalSpace

	#leftMax        like #left, but resize elements to max of them
	#leftSpaceMax   like #leftSpace, but resize elements
	#centerMax      like #center, but resize elements
	#rightMax       like #right, but resize elements to max of them
	#rightSpaceMax  like #rightSpace, but resize elements

    The defaults is #center for both directions.

    The layout is changed by the messages #verticalLayout: and #horizontalLayout:.
    For backward compatibility (to times, where only vLayout existed), the simple
    #layout: does the same as #verticalLayout:. Do not use this old method.

    The panel assumes, that the elements do not resize themselfes, after it
    became visible. This is not true for all widgets (buttons or labels may
    like to change). If you have changing elements, tell this to the panel
    with 'aPanel elementsChangeSize:true'. In that case, the panel will react
    to size changes, and reorganize things.

    If none of these layout/space combinations is exactly what you need in
    your application, create a subclass, and redefine the setChildPositions method.

    CAVEAT: this class started with #top and no horizontal alignments;
    as time went by, more layouts were added and the setup should be changed
    to use different selectors for space, max-resize and alignment
    (i.e. having more and more layout symbols makes things a bit confusing ...)
"
!

examples
"
    These examples demonstrate the effect of different layout
    settings.
    You should try more examples, combining spacing and different
    verticalLayout:/horizontalLayout: combinations.


    example: default layout (centered)

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'center (default)'.
	p := VerticalPanelView in:v.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'b2' in:p.
	b3 := Button label:'butt3' in:p.
	v extent:100 @ 300.
	v open


    example: horizontal centerMax

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'hL=centerMax'.
	p := VerticalPanelView in:v.
	p horizontalLayout:#centerMax.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'b2' in:p.
	b3 := Button label:'butt3' in:p.
	v extent:100 @ 300.
	v open


    example: horizontal leftMax

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'hL=leftMax'.
	p := VerticalPanelView in:v.
	p horizontalLayout:#leftMax.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'b2' in:p.
	b3 := Button label:'butt3' in:p.
	v extent:100 @ 300.
	v open


    example: horizontal leftSpaceMax

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'hL=leftMax'.
	p := VerticalPanelView in:v.
	p horizontalLayout:#leftSpaceMax.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'b2' in:p.
	b3 := Button label:'butt3' in:p.
	v extent:100 @ 300.
	v open


    example: horizontal rightMax

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'hL=rightMax'.
	p := VerticalPanelView in:v.
	p horizontalLayout:#rightMax.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'b2' in:p.
	b3 := Button label:'butt3' in:p.
	v extent:100 @ 300.
	v open


    example: horizontal rightSpaceMax

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'hL=rightMaxSpace'.
	p := VerticalPanelView in:v.
	p horizontalLayout:#rightSpaceMax.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'b2' in:p.
	b3 := Button label:'butt3' in:p.
	v extent:100 @ 300.
	v open


    example: top-layout

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'vL=top; hL=center (default)'.
	p := VerticalPanelView in:v.
	p verticalLayout:#top.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'butt2' in:p.
	b3 := Button label:'button3' in:p.
	v extent:100 @ 300.
	v open


    example: topSpace-layout

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'vL=topSpace; hL=center (default)'.
	p := VerticalPanelView in:v.
	p verticalLayout:#topSpace.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'butt2' in:p.
	b3 := Button label:'button3' in:p.
	v extent:100 @ 300.
	v open


    example: top-layout; horizontal fit

	|v p b1 b2 b3|

	v := StandardSystemView new.
	p := VerticalPanelView in:v.
	v label:'vL=top; hL=fit'.
	p verticalLayout:#top.
	p horizontalLayout:#fit.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'butt2' in:p.
	b3 := Button label:'button3' in:p.
	v extent:100 @ 300.
	v open


    example: top-layout; horizontal fit with space

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'vL=top; hL=fitSpace'.
	p := VerticalPanelView in:v.
	p verticalLayout:#top.
	p horizontalLayout:#fitSpace.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'butt2' in:p.
	b3 := Button label:'button3' in:p.
	v extent:100 @ 300.
	v open


    example: topSpace-layout; horizontal fit with space

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'vL=topSpace; hL=fitSpace'.
	p := VerticalPanelView in:v.
	p verticalLayout:#topSpace.
	p horizontalLayout:#fitSpace.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'butt2' in:p.
	b3 := Button label:'button3' in:p.
	v extent:100 @ 300.
	v open


    example: bottom-layout

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'vL=bottom; hL=center (default)'.
	p := VerticalPanelView in:v.
	p verticalLayout:#bottom.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'butt2' in:p.
	b3 := Button label:'button3' in:p.
	v extent:100 @ 300.
	v open


    example: bottomSpace-layout

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'vL=bottomSpace; hL=center (default)'.
	p := VerticalPanelView in:v.
	p verticalLayout:#bottomSpace.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'butt2' in:p.
	b3 := Button label:'button3' in:p.
	v extent:100 @ 300.
	v open


    example: spread-layout

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'vL=spread; hL=center (default)'.
	p := VerticalPanelView in:v.
	p verticalLayout:#spread.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'butt2' in:p.
	b3 := Button label:'button3' in:p.
	v extent:100 @ 300.
	v open


    example: spreadSpace-layout

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'vL=spreadSpace; hL=center (default)'.
	p := VerticalPanelView in:v.
	p verticalLayout:#spreadSpace.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'butt2' in:p.
	b3 := Button label:'button3' in:p.
	v extent:100 @ 300.
	v open


    example: fit-layout

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'vL=fit; hL=center (default)'.
	p := VerticalPanelView in:v.
	p verticalLayout:#fit.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'butt2' in:p.
	b3 := Button label:'button3' in:p.
	v extent:100 @ 300.
	v open


    example: fitSpace-layout

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'vL=fitSpace; hL=center (default)'.
	p := VerticalPanelView in:v.
	p verticalLayout:#fitSpace.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'butt2' in:p.
	b3 := Button label:'button3' in:p.
	v extent:100 @ 300.
	v open


    example: fully fitSpace

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'vL=fitSpace; hL=fitSpace'.
	p := VerticalPanelView in:v.
	p verticalLayout:#fitSpace.
	p horizontalLayout:#fitSpace.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'butt2' in:p.
	b3 := Button label:'button3' in:p.
	v extent:100 @ 300.
	v open


    example: from top, each at left:

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'vL=top; hL=left'.
	p := VerticalPanelView in:v.
	p verticalLayout:#top.
	p horizontalLayout:#left.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'butt2' in:p.
	b3 := Button label:'button3' in:p.
	v extent:100 @ 300.
	v open


    example: center, right:

	|v p b1 b2 b3|

	v := StandardSystemView new.
	v label:'vL=center; hL=right'.
	p := VerticalPanelView in:v.
	p verticalLayout:#center.
	p horizontalLayout:#right.
	p origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).
	b1 := Button label:'button1' in:p.
	b2 := Button label:'butt2' in:p.
	b3 := Button label:'button3' in:p.
	v extent:100 @ 300.
	v open


    example: a panel in a panel

	|v hp p b1 b2 b3|

	v := StandardSystemView new.

	hp := HorizontalPanelView in:v.
	hp verticalLayout:#fit.
	hp horizontalLayout:#fitSpace.
	hp origin:(0.0 @ 0.0) corner:(1.0 @ 1.0).

	1 to:3 do:[:i |
	    p := VerticalPanelView in:hp.
	    p borderWidth:0.
	    p verticalLayout:#fitSpace.
	    p horizontalLayout:#fit.
	    b1 := Button label:('button1-' , i printString) in:p.
	    b2 := Button label:('butt2-' , i printString) in:p.
	    b3 := Button label:('button3-' , i printString) in:p.
	].

	v extent:300 @ 100.
	v open


    example: checkToggles in a panel

	|panel|

	panel := VerticalPanelView new.
	panel horizontalLayout:#left.

	panel add:((CheckBox on:true asValue) label:'this is toggle number 1'; resize).
	panel add:((CheckBox on:false asValue) label:'nr 2 '; resize).
	panel add:((CheckBox on:true asValue) label:'number 3 '; resize).

	panel extent:(panel preferedExtent).
	panel open
"
! !

!VerticalPanelView methodsFor:'layout'!

setChildPositions
    "(re)compute position of every child"

    |ypos space sumOfHeights numChilds l hEach hInside maxWidth resizeToMax hL|

    subViews isNil ifTrue:[^ self].

    space := verticalSpace.
    numChilds := subViews size.
    hInside := height - (margin * 2) + (borderWidth*2) - subViews last borderWidth.

    vLayout == #fitSpace ifTrue:[
	"
	 adjust childs extents and set origins.
	 Be careful to avoid accumulation of rounding errors
	"
	hEach := (hInside - (numChilds + 1 * space)) / numChilds.
	ypos := space + margin - borderWidth.
    ] ifFalse:[
	vLayout == #fit ifTrue:[
	    "
	     adjust childs extents and set origins.
	     Be careful to avoid accumulation of rounding errors
	    "
	    hEach := (hInside - (numChilds - 1 * space)) / numChilds.
	    ypos := margin - borderWidth.
	] ifFalse:[
	    "
	     compute net height needed
	    "
	    sumOfHeights := subViews inject:0 into:[:sumSoFar :child | sumSoFar + child heightIncludingBorder].

	    l := vLayout.
	    ((l == #center) and:[numChilds == 1]) ifTrue:[
		l := #spread
	    ].
	    (l == #spread and:[numChilds == 1]) ifTrue:[
		l := #spreadSpace
	    ].

	    "
	     compute position of topmost subview and space between them;
	     if they do hardly fit, leave no space between them 
	    "
	    (sumOfHeights >= (height - (margin * 2))) ifTrue:[
		"
		 if we  have not enough space for all the elements, 
		 fill them tight, and show what can be shown (at least)
		"
		ypos := 0.
		space := 0
	    ] ifFalse:[
		((l == #bottom) or:[l == #bottomSpace]) ifTrue:[
		    ypos := height - (space * (numChilds - 1)) - sumOfHeights.
	"
		    borderWidth == 0 ifTrue:[
			ypos := ypos + space 
		    ].
	"           
		    l == #bottomSpace ifTrue:[
			ypos >= space ifTrue:[
			    ypos := ypos - space
			]
		    ].

		    ypos < 0 ifTrue:[
			space := space min:(height - sumOfHeights) // (numChilds + 1).
			ypos := height - (space * numChilds) - sumOfHeights.
		    ]
		] ifFalse: [
		    (l == #spread) ifTrue:[
			space := (height - sumOfHeights) // (numChilds - 1).
			ypos := 0.
			(space == 0) ifTrue:[
			    ypos := (height - sumOfHeights) // 2
			]
		    ] ifFalse: [
		      (l == #spreadSpace) ifTrue:[
			space := (height - sumOfHeights) // (numChilds + 1).
			ypos := space.
			(space == 0) ifTrue:[
			    ypos := (height - sumOfHeights) // 2
			]
		      ] ifFalse: [
			((l == #top) or:[l == #topSpace]) ifTrue:[
			    space := space min:(height - sumOfHeights) // (numChilds + 1).
			    l == #topSpace ifTrue:[
				ypos := space.
			    ] ifFalse:[
				ypos := 0
			    ]
			] ifFalse:[
			    "center"
			    ypos := (height - (sumOfHeights
						 + ((numChilds - 1) * space))) // 2.
			    ypos < 0 ifTrue:[
				space := (height - sumOfHeights) // (numChilds + 1).
				ypos := (height - (sumOfHeights
					       + ((numChilds - 1) * space))) // 2.
			    ]
			]
		      ]
		    ]
		]
	    ].
	].
    ].

    hL := hLayout.
    resizeToMax := false.
    (hLayout endsWith:'Max') ifTrue:[
	resizeToMax := true.
	maxWidth := subViews inject:0 into:[:maxSoFar :child | maxSoFar max:child widthIncludingBorder].
	hL == #centerMax ifTrue:[hL := #center].
	hL == #rightMax ifTrue:[hL := #right].
	hL == #rightSpaceMax ifTrue:[hL := #rightSpace].
	hL == #leftMax ifTrue:[hL := #left].
	hL == #leftSpaceMax ifTrue:[hL := #leftSpace].
    ].

    "
     now set positions
    "
    subViews do:[:child |
	|xpos bwChild wChild|

	wChild := child widthIncludingBorder.
	bwChild := child borderWidth.

	resizeToMax ifTrue:[
	    child width:(wChild := maxWidth - (bwChild  * 2)).
	].

	hL == #left ifTrue:[
	    xpos := 0
	] ifFalse:[
	    hL == #leftSpace ifTrue:[
		xpos := horizontalSpace
	    ] ifFalse:[
		hL == #right ifTrue:[
		    xpos := width - wChild
		] ifFalse:[
		    hL == #rightSpace ifTrue:[
			xpos := width - horizontalSpace - wChild.
		    ] ifFalse:[
			hL == #fitSpace ifTrue:[
			    xpos := horizontalSpace.
			    child width:(width - (horizontalSpace +  bwChild * 2))
			] ifFalse:[
			    hL == #fit ifTrue:[
				xpos := 0.
				child width:(width - (bwChild  * 2))
			    ] ifFalse:[
				"centered"
				 xpos := (width - wChild) // 2.
			    ]
			]
		    ]
		]
	    ]
	].
	(xpos < 0) ifTrue:[ xpos := 0 ].

	(vLayout == #fit or:[vLayout == #fitSpace]) ifTrue:[
	    child origin:(xpos @ ypos rounded)
		  corner:(xpos + (child width))
			 @ (ypos + hEach - bwChild) rounded.
	    ypos := ypos + hEach + space
	] ifFalse:[
	    child origin:(xpos@ypos).
	    ypos := ypos + (child heightIncludingBorder) + space
	]
    ]
! !

!VerticalPanelView methodsFor:'accessing'!

horizontalLayout
    "return the horizontal layout as symbol.
     the returned value is one of
	#left 
	#leftSpace 
	#center
	#right 
	#rightSpace 
	#fit 
      the default is #centered
    "

    ^ hLayout
!

horizontalLayout:aSymbol
    "change the horizontal layout as symbol.
     The argument, aSymbol must be one of:
	#left 
	#leftSpace 
	#center
	#right 
	#rightSpace 
	#fit 
      the default (if never changed) is #centered
    "

    (hLayout ~~ aSymbol) ifTrue:[
	hLayout := aSymbol.
	self layoutChanged
    ]
!

verticalLayout
    "return the vertical layout as a symbol.
     the returned value is one of
	#top
	#topSpace
	#spread
	#fit
	#center
	#bottom
	#bottomSpace
      the default is #centered
    "

    ^ vLayout
!

verticalLayout:aSymbol
    "change the vertical layout as a symbol.
     The argument, aSymbol must be one of:
	#top
	#topSpace
	#spread
	#fit
	#center
	#bottom
	#bottomSpace
      the default (if never changed) is #centered
    "

    (vLayout ~~ aSymbol) ifTrue:[
	vLayout := aSymbol.
	self layoutChanged
    ]
!

layout:aSymbol
    "leftover for historic reasons - do not use any more"

    self verticalLayout:aSymbol
!

layout
    "leftover for historic reasons - do not use any more"

    self verticalLayout
! !

!VerticalPanelView methodsFor:'queries'!

preferedExtent
    "return a good extent, one that makes subviews fit"

    |sumOfHeights maxWidth maxHeight|

    subViews isNil ifTrue:[^ horizontalSpace @ verticalSpace].

    "compute net height needed"

    sumOfHeights := 0.
    maxWidth := 0.
    maxHeight := 0.

    subViews do:[:child |
	|childsPreference|

	childsPreference := child preferedExtent.
	sumOfHeights := sumOfHeights + childsPreference y.
	maxHeight := maxHeight max:childsPreference y.
	maxWidth := maxWidth max:childsPreference x.

"/        sumOfHeights := sumOfHeights + child heightIncludingBorder.
"/        maxWidth := maxWidth max:(child widthIncludingBorder).
"/        maxHeight := maxHeight max:(child heightIncludingBorder).
    ].
    borderWidth ~~ 0 ifTrue:[
	sumOfHeights := sumOfHeights + (horizontalSpace * 2).
	maxWidth := maxWidth + (horizontalSpace * 2).
    ].
    (vLayout == #fit or:[vLayout == #fitSpace]) ifTrue:[
	sumOfHeights := maxHeight * subViews size.
	borderWidth ~~ 0 ifTrue:[
	    sumOfHeights := sumOfHeights + (verticalSpace * 2).
	]
    ] ifFalse:[
	sumOfHeights := sumOfHeights + ((subViews size - 1) * verticalSpace).
    ].

    ((hLayout == #leftSpace) or:[hLayout == #rightSpace]) ifTrue:[
	maxWidth := maxWidth + horizontalSpace
    ] ifFalse:[
	((hLayout == #fitSpace) or:[hLayout == #center]) ifTrue:[
	    maxWidth := maxWidth + (horizontalSpace * 2)
	]        
    ].
    ^ maxWidth @ sumOfHeights
! !