'From Cuis 2.6 of 10 August 2010 [latest update: #540] on 27 August 2010 at 11:26:08 am'!
!classDefinition: #FixedLayoutSpec category: #'Morphic-Layouts'!
Object subclass: #FixedLayoutSpec
	instanceVariableNames: 'fixedWidth fixedHeight minorDirectionPadding'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Morphic-Layouts'!

!FixedLayoutSpec commentStamp: '<historical>' prior: 0!
LayoutSpecs are the basis for a new layout mechanism, alternative to LayoutFrame. Any Morph can be given a LayoutSpec, but in order to honor it, the morph must be submorph of a LayoutMorph.

A LayoutSpec specifies how a morph wants to be layed out. It can specify either a fixed width or a fraction of some available owner width. Same goes for height.

FixedLayoutSpec can only handle fixed height and width. ProportionalLayoutSpec han handle fixed or proportional (with some minimum) extent on either direction.!

!classDefinition: #LayoutMorphTest category: #'Morphic-Tests'!
TestCase subclass: #LayoutMorphTest
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Morphic-Tests'!
!classDefinition: #LayoutSpec category: #'Morphic-Layouts'!
FixedLayoutSpec subclass: #LayoutSpec
	instanceVariableNames: 'proportionalWidth proportionalHeight'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Morphic-Layouts'!

!LayoutSpec commentStamp: 'jmv 8/25/2010 13:57' prior: 0!
a ProportionalLayoutSpec specifies how much of available space in owner we want for our owning morph. In this case, fixedWidth / fixedHeight act as the minimum extent we desire.!

!classDefinition: #LayoutMorph category: #'Morphic-Layouts'!
RectangleMorph subclass: #LayoutMorph
	instanceVariableNames: 'direction separation padding'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Morphic-Layouts'!

!LayoutMorph commentStamp: '<historical>' prior: 0!
A row or column of widgets, does layout by placing them either horizontally or vertically.!


!Collection methodsFor: 'math functions' stamp: 'jmv 8/24/2010 22:48'!
sum: aBlock
	"This is implemented using a variant of the normal inject:into: pattern. 
	The reason for this is that it is not known whether we're in the normal 
	number line, i.e. whether 0 is a good initial value for the sum. 
	Consider a collection of measurement objects, 0 would be the unitless 
	value and would not be appropriate to add with the unit-ed objects."
	| sum sample |
	sample _ aBlock value: self anyOne.
	sum _ self inject: sample into: [ :previousValue :each |  previousValue + (aBlock value: each) ].
	^ sum - sample! !


!FixedLayoutSpec methodsFor: 'initialization' stamp: 'jmv 8/25/2010 10:11'!
initialize
	"Just some reasonable defaults"
	fixedWidth _ 0.
	fixedHeight _ 0.
	minorDirectionPadding _ 0.5! !

!FixedLayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/25/2010 10:12'!
fixedHeight: aNumber
	fixedHeight _ aNumber! !

!FixedLayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/27/2010 10:31'!
fixedHeightFor: aMorph
	^fixedHeight ifNil: [ aMorph height ]! !

!FixedLayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/25/2010 10:16'!
fixedWidth: aNumber
	fixedWidth _ aNumber! !

!FixedLayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/27/2010 10:32'!
fixedWidthFor: aMorph
	^fixedWidth ifNil: [ aMorph width ]! !

!FixedLayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/25/2010 10:53'!
minorDirectionPadding
	^minorDirectionPadding! !

!FixedLayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/25/2010 14:02'!
minorDirectionPadding: aSymbolOrNumber
	"This sets how padding is done in the secondary direction. For instance, if the owning morph is set in a row, the row will control horizontal layout. But if there is unused vertical space, it will be used according to this parameter. For instance, #top sets the owning morph at the top. Same for #bottom and #center. If the owner is contained in a column, #left, #center or #right should be used. Alternatively, any number between 0.0 and 1.0 can be used.
	self new minorDirectionPadding: #center
	self new minorDirectionPadding: 0.9
	"
	minorDirectionPadding _ aSymbolOrNumber
		caseOf: {
			[ #top ] -> [ 0.0 ].
			[ #left ] -> [ 0.0 ].
			[ #center ] -> [ 0.5 ].
			[ #right ] -> [ 1.0 ].
			[ #bottom ] -> [ 1.0 ]
		}
		otherwise: [ aSymbolOrNumber ]! !

!FixedLayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/25/2010 10:27'!
proportionalHeight
	^0! !

!FixedLayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/25/2010 10:27'!
proportionalWidth
	^0! !

!FixedLayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/27/2010 10:26'!
useMorphHeight
	"Do not attempt to layout width. Use current morph width if possible."
	fixedHeight _ nil! !

!FixedLayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/27/2010 10:26'!
useMorphWidth
	"Do not attempt to layout width. Use current morph width if possible."
	fixedWidth _ nil! !

!FixedLayoutSpec methodsFor: 'layout' stamp: 'jmv 8/27/2010 10:24'!
heightOf: aMorph for: availableSpace
	fixedHeight ifNil: [ aMorph height ].
	^fixedHeight! !

!FixedLayoutSpec methodsFor: 'layout' stamp: 'jmv 8/27/2010 10:23'!
widthOf: aMorph for: availableSpace
	fixedWidth ifNil: [ aMorph width ].
	^fixedWidth! !

!FixedLayoutSpec methodsFor: 'testing' stamp: 'jmv 8/25/2010 11:35'!
is: aSymbol
	^aSymbol = #LayoutSpec or: [ super is: aSymbol ]! !


!LayoutMorphTest methodsFor: 'tests' stamp: 'jmv 8/27/2010 10:04'!
testLayout1
	"
	self new testLayout1
	"
	| pane row1 row2 row3 r1c1 r1c2 r1c3 r1c4 r1c5 r2c1 r2c2 r2c3 r3c1 r3c2 r3c3 |
	pane _ LayoutMorph newColumn.
	pane color: Color red.
	row1 _ LayoutMorph newRow.
	row1 color: Color red;
		addMorph: (r1c1 _ RectangleMorph new color: (Color h: 60 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec fixedWidth: 10);
		addMorph: (r1c2 _ RectangleMorph new color: Color blue)
			layoutSpec: (LayoutSpec proportionalWidth: 0.8);
		addMorph: (r1c3 _ RectangleMorph new color: (Color h: 30 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec proportionalWidth: 0.4);
		addMorph: (r1c4 _ RectangleMorph new color: (Color h: 30 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec proportionalWidth: 0.15);
		addMorph: (r1c5 _ RectangleMorph new color: (Color h: 60 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec fixedWidth: 20 fixedHeight: 20).
	pane addMorph: row1 layoutSpec: LayoutSpec useAll.
	row2 _ LayoutMorph newRow.
	row2 color: Color red;
		addMorph: (r2c1 _ RectangleMorph new color: Color blue)
			layoutSpec: (LayoutSpec proportionalWidth: 0.8);
		addMorph: (r2c2 _ RectangleMorph new color: (Color h: 30 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec proportionalWidth: 0.4);
		addMorph: (r2c3 _ RectangleMorph new color: (Color h: 30 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec proportionalWidth: 0.2).
	pane addMorph: row2 layoutSpec: LayoutSpec useAll.
	row3 _ LayoutMorph newRow.
	row3 color: Color red;
		addMorph: (r3c1 _ RectangleMorph new color: (Color h: 120 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec fixedWidth: 20 proportionalHeight: 0.8);
		addMorph: (r3c2 _ RectangleMorph new color: (Color h: 90 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec proportionalWidth: 0.5 fixedHeight: 40);
		addMorph: (r3c3 _ RectangleMorph new color: (Color h: 150 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec fixedWidth: 30 proportionalHeight: 1.0).
	pane addMorph: row3 layoutSpec: (LayoutSpec fixedHeight: 60).
	pane openInWorld; extent: 408@300.
	World doOneCycleNow.

	self assert: row1 width = (pane width - 10).
	self assert: r1c1 width class == SmallInteger.
	self assert: r1c1 height class == SmallInteger.
	self assert: r1c1 width = 10.
	self assert: r1c1 height = (row1 height - 10).
	self assert: r1c2 width = 200.
	self assert: r1c2 height = (row1 height - 10).
	self assert: r1c3 width = (r1c2 width / 0.8 * 0.4) rounded.
	self assert: r1c3 height = (row1 height - 10).
	self assert: r1c4 width = (r1c2 width / 0.8 * 0.15) rounded.
	self assert: r1c4 height = (row1 height - 10).
	self assert: r1c5 width = 20.
	self assert: r1c5 height = 20.

	self assert: row2 width = (pane width - 10).
	self assert: r2c1 width = 216.
	self assert: r2c1 height = (row2 height - 10).
	self assert: r2c2 width = (r2c1 width / 0.8 * 0.4) rounded.
	self assert: r2c2 height = (row2 height - 10).
	self assert: r2c3 width = (r2c1 width / 0.8 * 0.2) rounded.
	self assert: r2c3 height = (row2 height - 10).

	self assert: row3 width = (pane width - 10).
	self assert: row3 height = 60.
	self assert: r3c1 width = 20.
	self assert: r3c1 height = (row3 height - 10 * 0.8) rounded.
	self assert: r3c2 width = (row3 width - 10 - 20 - 10 - 30 * 0.5) rounded.
	self assert: r3c2 height = 40.
	self assert: r3c3 width = 30.
	self assert: r3c3 height = (row3 height - 10).

	pane delete! !

!LayoutMorphTest methodsFor: 'tests' stamp: 'jmv 8/27/2010 10:05'!
testLayout2
	"
	self new testLayout2
	"
	| pane row c1 c2 c3 |
	pane _ LayoutMorph newColumn.
	pane color: Color red.
	row _ LayoutMorph newRow.
	row
		color: (Color h: 270 s: 0.2 v: 0.6);
		addMorph: (c1 _ RectangleMorph new color: (Color h: 120 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec fixedWidth: 20 proportionalHeight: 0.8 minorDirectionPadding: #bottom);
		addMorph: (c2 _ RectangleMorph new color: (Color h: 90 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec proportionalWidth: 0.8 fixedHeight: 40 minorDirectionPadding: #top);
		addMorph: (c3 _ RectangleMorph new color: (Color h: 150 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec fixedWidth: 30 proportionalHeight: 0.7 minorDirectionPadding: #center).
	pane addMorph: row layoutSpec: (LayoutSpec proportionalHeight: 0.9).
	pane openInWorld; extent: 400@300.
	World doOneCycleNow.

	self assert: row width = (pane width - 10).
	self assert: row height = (pane height - 10 * 0.9) rounded.
	self assert: c1 bottom = (row bottom - 5) description: 'Should be at bottom'.
	self assert: c1 width = 20.
	self assert: c1 height = (row height - 10 * 0.8) rounded.
	self assert: c2 top = (row top + 5) description: 'Should be at top'.
	self assert: c2 width = 256.
	self assert: c2 height = 40.
	self assert: (c3 top - row top) = (row bottom - c3 bottom) description: 'Should be centered'.
	self assert: c3 width = 30.
	self assert: c3 height = (c1 height / 0.8 * 0.7) truncated. "truncated and not rounding... no big deal"

	pane delete! !

!LayoutMorphTest methodsFor: 'tests' stamp: 'jmv 8/27/2010 10:05'!
testLayout3
	"
	self new testLayout3
	"
	| pane row innerRow i1 i2 i3 c2 c3 |
	pane _ LayoutMorph newColumn.
	pane color: Color red.
	row _ LayoutMorph newRow.
	innerRow _ LayoutMorph newRow color: Color red.
	innerRow
		addMorph: (i1 _ RectangleMorph new  borderWidth: 0)
			layoutSpec: (LayoutSpec fixedWidth: 10 fixedHeight: 10);
		addMorph: (i2 _ RectangleMorph new  borderWidth: 0)
			layoutSpec: (LayoutSpec fixedWidth: 10 fixedHeight: 10);
		addMorph: (i3 _ RectangleMorph new  borderWidth: 0)
			layoutSpec: (LayoutSpec proportionalWidth: 1.0 fixedHeight: 10).
	row
		color: (Color h: 270 s: 0.2 v: 0.6);
		addMorph: innerRow
			layoutSpec: (LayoutSpec proportionalWidth: 0.5 fixedHeight: 30 minorDirectionPadding: #center);
		addMorph: (c2 _ RectangleMorph new color: (Color h: 90 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec proportionalWidth: 0.5 fixedHeight: 40 minorDirectionPadding: #top);
		addMorph: (c3 _ RectangleMorph new color: (Color h: 150 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec fixedWidth: 30 proportionalHeight: 1.0).
	pane addMorph: row layoutSpec: (LayoutSpec fixedHeight: 200).
	pane openInWorld; extent: 400@300.
	World doOneCycleNow.

	self assert: row left = (pane left + 5).
	self assert: row width = (pane width - 10).
	self assert: row height = 200.
	self assert: innerRow left = (row left + 5).
	self assert: (innerRow top - row top) = (row bottom - innerRow bottom) description: 'Should be centered'.
	self assert: innerRow width = (pane width - 10 - 10 - 30 - 10 * 0.5) rounded.
	self assert: innerRow height = 30.

	self assert: i1 left = (innerRow left + 5).
	self assert: (i1 top - innerRow top) = (innerRow bottom - i1 bottom) description: 'Should be centered'.
	self assert: i1 width = 10.
	self assert: i1 height = 10.
	self assert: i2 left = (innerRow left + 20).
	self assert: (i2 top - innerRow top) = (innerRow bottom - i2 bottom) description: 'Should be centered'.
	self assert: i2 width = 10.
	self assert: i2 height = 10.
	self assert: i3 left = (innerRow left + 35).
	self assert: (i3 top - innerRow top) = (innerRow bottom - i3 bottom) description: 'Should be centered'.
	self assert: i3 width = (innerRow width - 40).
	self assert: i3 height = 10.

	self assert: c2 top = (row top + 5) description: 'Should be at top'.
	self assert: c2 width = (pane width - 10 - 10 - 30 - 10 * 0.5) rounded.
	self assert: c2 height = 40.
	self assert: (c3 top - row top) = (row bottom - c3 bottom) description: 'Should be centered'.
	self assert: c3 width = 30.
	self assert: c3 height = (row height - 10).

	pane delete! !


!LayoutSpec methodsFor: 'initialization' stamp: 'jmv 8/25/2010 13:54'!
initialize
	"Just some reasonable defaults"
	super initialize.
	proportionalWidth _ 1.0.
	proportionalHeight _ 1.0! !

!LayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/25/2010 13:54'!
fixedHeight: aNumber
	"aNumber is taken as the fixed height to use"
	fixedHeight _ aNumber.
	proportionalHeight _ 0.0! !

!LayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/27/2010 10:32'!
fixedHeightFor: aMorph
	"We use the fixedHeight ivar to store the minimum. We don't have a fixedHeight.
	(unless proportional is zero, then we do have fixedHeight)"
	fixedHeight ifNil: [ ^aMorph height ].
	^proportionalHeight = 0.0
		ifTrue: [ fixedHeight ]
		ifFalse: [ 0 ]! !

!LayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/25/2010 13:54'!
fixedWidth: aNumber
	"aNumber is taken as the fixed width to use"
	fixedWidth _ aNumber.
	proportionalWidth _ 0.0! !

!LayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/27/2010 10:33'!
fixedWidthFor: aMorph
	"We use the fixedWidth ivar to store the minimum. We don't have a fixedWidth.
	(unless proportional is zero, then we do have fixedWidth)"
	fixedWidth ifNil: [ ^aMorph width ].
	^proportionalWidth = 0.0
		ifTrue: [ fixedWidth ]
		ifFalse: [ 0 ]! !

!LayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/25/2010 13:54'!
proportionalHeight
	^proportionalHeight! !

!LayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/25/2010 13:55'!
proportionalHeight: aNumber
	^self proportionalHeight: aNumber minimum: 0.0! !

!LayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/25/2010 13:55'!
proportionalHeight: aNumber minimum: otherNumber
	"otherNumber is taken as the minimum desired height"
	proportionalHeight _ aNumber.
	fixedHeight _ otherNumber! !

!LayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/25/2010 13:55'!
proportionalWidth
	^proportionalWidth! !

!LayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/25/2010 13:55'!
proportionalWidth: aNumber
	^self proportionalWidth: aNumber minimum: 0! !

!LayoutSpec methodsFor: 'accessing' stamp: 'jmv 8/25/2010 13:55'!
proportionalWidth: aNumber minimum: otherNumber
	"otherNumber is taken as the minimum desired width"
	proportionalWidth _ aNumber.
	fixedWidth _ otherNumber! !

!LayoutSpec methodsFor: 'layout' stamp: 'jmv 8/27/2010 10:30'!
heightOf: aMorph for: availableSpace
	fixedHeight ifNil: [ ^aMorph height ].
	^fixedHeight max: proportionalHeight * availableSpace! !

!LayoutSpec methodsFor: 'layout' stamp: 'jmv 8/27/2010 10:30'!
widthOf: aMorph for: availableSpace
	fixedWidth ifNil: [ ^aMorph width ].
	^fixedWidth max: proportionalWidth * availableSpace! !


!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 14:07'!
fixedHeight: aNumber
	"Will use all available width"
	^self new fixedHeight: aNumber! !

!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 14:07'!
fixedWidth: aNumber
	"Will use all available height"
	^self new fixedWidth: aNumber! !

!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 14:17'!
fixedWidth: aNumber fixedHeight: otherNumber
	"A simpler and cheaper FixedLayoutSpec will suffice"
	^ FixedLayoutSpec new
		fixedWidth: aNumber;
		fixedHeight: otherNumber! !

!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 14:17'!
fixedWidth: aNumber fixedHeight: otherNumber minorDirectionPadding: aSymbolOrNumber
	"A simpler and cheaper FixedLayoutSpec will suffice"
	^ FixedLayoutSpec new
		fixedWidth: aNumber;
		fixedHeight: otherNumber;
		minorDirectionPadding: aSymbolOrNumber! !

!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 14:12'!
fixedWidth: aNumber proportionalHeight: otherNumber

	^self new
		fixedWidth: aNumber;
		proportionalHeight: otherNumber! !

!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 14:18'!
fixedWidth: aNumber proportionalHeight: otherNumber minorDirectionPadding: aSymbolOrNumber

	^self new
		fixedWidth: aNumber;
		proportionalHeight: otherNumber;
		minorDirectionPadding: aSymbolOrNumber! !

!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/27/2010 10:27'!
morphHeightProportionalWidth: aNumber

	^self new
		proportionalWidth: aNumber;
		useMorphHeight! !

!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/27/2010 10:28'!
morphWidthProportionalHeight: aNumber

	^self new
		useMorphWidth;
		proportionalHeight: aNumber! !

!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 14:07'!
proportionalHeight: aNumber
	"Will use all available width"
	^self new proportionalHeight: aNumber! !

!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 14:07'!
proportionalWidth: aNumber
	"Will use all available height"
	^self new proportionalWidth: aNumber! !

!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 14:13'!
proportionalWidth: aNumber fixedHeight: otherNumber

	^self new
		proportionalWidth: aNumber;
		fixedHeight: otherNumber! !

!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 14:18'!
proportionalWidth: aNumber fixedHeight: otherNumber minorDirectionPadding: aSymbolOrNumber

	^self new
		proportionalWidth: aNumber;
		fixedHeight: otherNumber;
		minorDirectionPadding: aSymbolOrNumber! !

!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 14:18'!
proportionalWidth: aNumber proportionalHeight: otherNumber

	^self new
		proportionalWidth: aNumber;
		proportionalHeight: otherNumber! !

!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 14:18'!
proportionalWidth: aNumber proportionalHeight: otherNumber minorDirectionPadding: aSymbolOrNumber

	^self new
		proportionalWidth: aNumber;
		proportionalHeight: otherNumber;
		minorDirectionPadding: aSymbolOrNumber! !

!LayoutSpec class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 14:09'!
useAll
	"Use all available space"
	^self new! !


!LightWidget methodsFor: 'change reporting' stamp: 'jmv 8/24/2010 13:51'!
invalidRect: aRectangle from: aMorph
	| damageRect |
	owner ifNotNil: [
		aRectangle hasPositiveExtent ifFalse: [ ^ self ].
		damageRect _ aRectangle.
		aMorph == self ifFalse: [
			"Clip to receiver's clipping bounds if the damage came from a child"
			damageRect _ aRectangle intersect: bounds ].
		owner
			invalidRect: damageRect
			from: self ].! !


!Morph methodsFor: 'change reporting' stamp: 'jmv 8/24/2010 13:51'!
invalidRect: aRectangle from: aMorph
	| damageRect |
	owner ifNotNil: [
		aRectangle hasPositiveExtent ifFalse: [ ^ self ].
		damageRect _ aRectangle.
		aMorph == self ifFalse: [
			"Clip to receiver's clipping bounds if the damage came from a child"
			self clipsSubmorphs ifTrue: [
				damageRect _ aRectangle intersect: self clippingBounds ]].
		owner
			invalidRect: damageRect
			from: self ]! !

!Morph methodsFor: 'layout-properties' stamp: 'jmv 8/25/2010 10:18'!
layoutSpec
	"Layout specific. Return the layout spec describing where the  
	receiver should appear in a proportional layout"
	^ self hasExtension
		ifTrue: [ self extension layoutSpec]! !

!Morph methodsFor: 'layout-properties' stamp: 'jmv 8/25/2010 10:19'!
layoutSpec: aLayoutSpec
	"Layout specific. Set the layout spec describing where the receiver should appear in a proportional layout"
	self layoutSpec == aLayoutSpec ifTrue:[^self].
	self assureExtension layoutSpec: aLayoutSpec.
	self layoutChanged.! !


!LayoutMorph methodsFor: 'initialization' stamp: 'jmv 8/27/2010 10:01'!
defaultBorderWidth
	^0! !

!LayoutMorph methodsFor: 'initialization' stamp: 'jmv 8/27/2010 10:01'!
defaultColor
	^Color white! !

!LayoutMorph methodsFor: 'initialization' stamp: 'jmv 8/25/2010 10:04'!
initialize
	super initialize.
	separation _ 5! !

!LayoutMorph methodsFor: 'initialization' stamp: 'jmv 8/25/2010 10:04'!
initializeAsColumn
	self initialize.
	direction _ #vertical.
	self padding: #center! !

!LayoutMorph methodsFor: 'initialization' stamp: 'jmv 8/25/2010 10:04'!
initializeAsRow
	self initialize.
	direction _ #horizontal.
	self padding: #left! !

!LayoutMorph methodsFor: 'accessing' stamp: 'jmv 8/25/2010 14:02'!
padding: aSymbolOrNumber
	"This sets how extra space is used when doing layout. For example, a column might have extra , unneded vertical space. #top means widgets are set close to the top, and extra space is at bottom. Conversely, #bottom means widgets are set close to the bottom, and extra space is at top. Valid values include #left and #right (for rows) and #center. Alternatively, any number between 0.0 and 1.1 might be used.
	self new padding: #center
	self new padding: 0.9
	"
	padding _ aSymbolOrNumber
		caseOf: {
			[ #top ] -> [ 0.0 ].
			[ #left ] -> [ 0.0 ].
			[ #center ] -> [ 0.5 ].
			[ #right ] -> [ 1.0 ].
			[ #bottom ] -> [ 1.0 ]
		}
		otherwise: [ aSymbolOrNumber ]! !

!LayoutMorph methodsFor: 'accessing' stamp: 'jmv 8/27/2010 10:13'!
separation: aNumber
	separation _ aNumber! !

!LayoutMorph methodsFor: 'layout' stamp: 'jmv 8/27/2010 10:33'!
layoutSubmorphsHorizontallyIn: boundsForLayout
	"Compute a new layout based on the given layout bounds."
	| usableWidth sumOfFixed sumOfProportional normalizationFactor availableForPropWidth widths l usableHeight boundsTop boundsRight r t b |
	usableWidth _ boundsForLayout width - ((submorphs size + 1) * separation).
	sumOfFixed _ submorphs sum: [ :m | m layoutSpec fixedWidthFor: m ].
	sumOfProportional _ submorphs sum: [ :m | m layoutSpec proportionalWidth ].
	normalizationFactor _ 1.0 / (sumOfProportional max: 1.0).
	availableForPropWidth _ (usableWidth - sumOfFixed) * normalizationFactor.
	widths _ submorphs collect: [ :m | m layoutSpec widthOf: m for: availableForPropWidth ].
	l _ ((usableWidth - widths sum) * padding + separation max: 0) +  boundsForLayout left.
	usableHeight _ boundsForLayout height - (2*separation) max: 0.
	boundsTop _ boundsForLayout top.	
	boundsRight _ boundsForLayout right.
	submorphs
		with: widths
		do: [ :m :w | | ls h |
			"major direction"
			r _ l + w min: boundsRight.
			"minor direction"
			ls _ m layoutSpec.
			h _ (ls heightOf: m for: usableHeight) min: usableHeight.
			t _ (usableHeight - h) * ls minorDirectionPadding + separation + boundsTop.
			b _ t + h.
			"Set bounds and adjust major direction for next step"
			m bounds: (l rounded @ t rounded corner: r rounded @ b rounded).
			w > 0 ifTrue: [
				l _ r + separation min: boundsRight ]]! !

!LayoutMorph methodsFor: 'layout' stamp: 'jmv 8/25/2010 10:04'!
layoutSubmorphsIn: layoutBounds
	"Compute a new layout based on the given layout bounds."

	submorphs isEmpty ifTrue: [
		^fullBounds _ bounds].

	direction == #horizontal ifTrue: [
		self layoutSubmorphsHorizontallyIn: layoutBounds ].

	direction == #vertical ifTrue: [
		self layoutSubmorphsVerticallyIn: layoutBounds ].

	fullBounds _ self computeFullBounds! !

!LayoutMorph methodsFor: 'layout' stamp: 'jmv 8/27/2010 10:33'!
layoutSubmorphsVerticallyIn: boundsForLayout
	"Compute a new layout based on the given layout bounds."
	| usableHeight sumOfFixed sumOfProportional normalizationFactor availableForPropHeight heights t usableWidth boundsLeft boundsBottom b l r |
	usableHeight _ boundsForLayout height - ((submorphs size + 1) * separation).
	sumOfFixed _ submorphs sum: [ :m | m layoutSpec fixedHeightFor: m ].
	sumOfProportional _ submorphs sum: [ :m | m layoutSpec proportionalHeight ].
	normalizationFactor _ 1.0 / (sumOfProportional max: 1.0).
	availableForPropHeight _ (usableHeight - sumOfFixed) * normalizationFactor.
	heights _ submorphs collect: [ :m | m layoutSpec heightOf: m for: availableForPropHeight ].
	t _ ((usableHeight - heights sum) * padding + separation max: 0) +  boundsForLayout top.
	usableWidth _ boundsForLayout width - (2*separation) max: 0.
	boundsLeft _ boundsForLayout left.	
	boundsBottom _ boundsForLayout bottom.
	submorphs
		with: heights
		do: [ :m :h | | ls w |
			"major direction"
			b _ t + h min: boundsBottom.
			"minor direction"
			ls _ m layoutSpec.
			w _ (ls widthOf: m for: usableWidth) min: usableWidth.
			l _ (usableWidth - w) * ls minorDirectionPadding + separation + boundsLeft.
			r _ l + w.
			"Set bounds and adjust major direction for next step"
			m bounds: (l rounded @ t rounded corner: r rounded @ b rounded).
			h > 0 ifTrue: [
				t _ b + separation min: boundsBottom ]]! !

!LayoutMorph methodsFor: 'submorphs-add/remove' stamp: 'jmv 8/25/2010 10:20'!
addMorph: aMorph layoutSpec: aLayoutSpec
	"Add a submorph, at the bottom or right, with aLayoutSpec"
	aMorph layoutSpec: aLayoutSpec.
	self addMorphBack: aMorph! !


!LayoutMorph class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 10:04'!
new
	self error: 'Please specify whether you want a row or a column.'! !

!LayoutMorph class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 10:04'!
newColumn
	^self basicNew initializeAsColumn! !

!LayoutMorph class methodsFor: 'instance creation' stamp: 'jmv 8/25/2010 10:04'!
newRow
	^self basicNew initializeAsRow! !

!LayoutMorph class methodsFor: 'examples' stamp: 'jmv 8/27/2010 10:03'!
example1
"
	self example1
"
| pane row |
pane _ LayoutMorph newColumn.
pane color: Color red.

row _ LayoutMorph newRow.
row
	color: Color red;
	addMorph: (RectangleMorph new color: (Color h: 60 s: 0.6 v: 0.6))
		layoutSpec: (LayoutSpec fixedWidth: 10); 
	addMorph: (RectangleMorph new color: (Color blue))
		layoutSpec: (LayoutSpec proportionalWidth: 0.8);
	addMorph: (RectangleMorph new color: (Color h: 30 s: 0.6 v: 0.6))
		layoutSpec: (LayoutSpec proportionalWidth: 0.4);
	addMorph: (RectangleMorph new color: (Color h: 30 s: 0.6 v: 0.6))
		layoutSpec: (LayoutSpec proportionalWidth: 0.15);
	addMorph: (RectangleMorph new color: (Color h: 60 s: 0.6 v: 0.6))
		layoutSpec: (LayoutSpec fixedWidth: 20 fixedHeight: 20).
pane addMorph: row layoutSpec: LayoutSpec useAll.

row _ LayoutMorph newRow.
row
	color: Color red;
	addMorph: (RectangleMorph new color: (Color blue))
		layoutSpec: (LayoutSpec proportionalWidth: 0.8);
	addMorph: (RectangleMorph new color: (Color h: 30 s: 0.6 v: 0.6))
		layoutSpec: (LayoutSpec proportionalWidth: 0.4);
	addMorph: (RectangleMorph new color: (Color h: 30 s: 0.6 v: 0.6))
		layoutSpec: (LayoutSpec proportionalWidth: 0.2).
pane addMorph: row layoutSpec: LayoutSpec useAll.

row _ LayoutMorph newRow.
row
	color: Color red;
	addMorph: (RectangleMorph new color: (Color h: 120 s: 0.6 v: 0.6))
		layoutSpec: (LayoutSpec fixedWidth: 20 proportionalHeight: 0.8);
	addMorph: (RectangleMorph new color: (Color h: 90 s: 0.6 v: 0.6))
		layoutSpec: (LayoutSpec  proportionalWidth: 0.5 fixedHeight: 40);
	addMorph: (RectangleMorph new color: (Color h: 150 s: 0.6 v: 0.6))
		layoutSpec: (LayoutSpec fixedWidth: 30 proportionalHeight: 1.0).
pane addMorph: row layoutSpec: (LayoutSpec fixedHeight: 60).
pane openInWorld! !

!LayoutMorph class methodsFor: 'examples' stamp: 'jmv 8/27/2010 10:05'!
example2
	"
	self example2
	"
	| pane row |
	pane _ LayoutMorph newColumn.
	pane color: Color red.
	row _ LayoutMorph newRow.
	row
		color: (Color h: 270 s: 0.2 v: 0.6);
		addMorph: (RectangleMorph new color: (Color h: 120 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec fixedWidth: 20 proportionalHeight: 0.8 minorDirectionPadding: #bottom);
		addMorph: (RectangleMorph new color: (Color h: 90 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec proportionalWidth: 0.8 fixedHeight: 40 minorDirectionPadding: #top);
		addMorph: (RectangleMorph new color: (Color h: 150 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec fixedWidth: 30 proportionalHeight: 0.7 minorDirectionPadding: #center).
	pane addMorph: row layoutSpec: (LayoutSpec proportionalHeight: 0.9).
	pane openInWorld! !

!LayoutMorph class methodsFor: 'examples' stamp: 'jmv 8/27/2010 10:05'!
example3
	"
	self example3
	"
	| pane row innerRow |
	pane _ LayoutMorph newColumn.
	pane color: Color red.
	row _ LayoutMorph newRow.
	innerRow _ LayoutMorph newRow color: Color red.
	innerRow
		addMorph: RectangleMorph new layoutSpec: (LayoutSpec fixedWidth: 10 fixedHeight: 10);
		addMorph: RectangleMorph new layoutSpec: (LayoutSpec fixedWidth: 10 fixedHeight: 10);
		addMorph: RectangleMorph new layoutSpec: (LayoutSpec proportionalWidth: 1.0 fixedHeight: 10).
	row
		color: (Color h: 270 s: 0.2 v: 0.6);
		addMorph: innerRow
			layoutSpec: (LayoutSpec proportionalWidth: 0.5 fixedHeight: 30 minorDirectionPadding: #center);
		addMorph: (RectangleMorph new color: (Color h: 90 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec proportionalWidth: 0.5 fixedHeight: 40 minorDirectionPadding: #top);
		addMorph: (RectangleMorph new color: (Color h: 150 s: 0.6 v: 0.6))
			layoutSpec: (LayoutSpec fixedWidth: 30 proportionalHeight: 1.0).
	pane addMorph: row layoutSpec: (LayoutSpec proportionalHeight: 0.9).
	pane openInWorld! !


!MorphExtension methodsFor: 'accessing - basic properties' stamp: 'jmv 8/25/2010 10:19'!
layoutSpec
	^ layoutSpec ifNotNil: [
		(layoutSpec is: #LayoutSpec) ifTrue: [ layoutSpec ]].! !

!MorphExtension methodsFor: 'accessing - basic properties' stamp: 'jmv 8/25/2010 10:19'!
layoutSpec: aLayoutSpec

	layoutSpec _ aLayoutSpec! !
