diff --git a/src/Toplo-Examples/ToSandBox.class.st b/src/Toplo-Examples/ToSandBox.class.st index cd007c731..ef988a53d 100644 --- a/src/Toplo-Examples/ToSandBox.class.st +++ b/src/Toplo-Examples/ToSandBox.class.st @@ -9652,12 +9652,12 @@ ToSandBox class >> tripletWith2IconsAnd1Label [ | but | but := ToTripletElement new. but startElement: (BlElement new - size: 25 asPoint; + extent: 25 asPoint; background: Color red; yourself). but middleElement: (ToLabel text: 'Middle'). but endElement: (BlElement new - size: 25 asPoint; + extent: 25 asPoint; background: Color red; yourself). ^ but diff --git a/src/Toplo/BlLayoutCommonConstraints.extension.st b/src/Toplo/BlLayoutCommonConstraints.extension.st index 274a04078..36b007745 100644 --- a/src/Toplo/BlLayoutCommonConstraints.extension.st +++ b/src/Toplo/BlLayoutCommonConstraints.extension.st @@ -24,3 +24,9 @@ BlLayoutCommonConstraints >> infiniteDo: aValuable [ self hasInfinite ifFalse: [ ^ self ]. aValuable value: self infinite ] + +{ #category : #'*Toplo' } +BlLayoutCommonConstraints >> triplet [ + + ^ self at: ToTripletLayout +] diff --git a/src/Toplo/TToTripletElement.trait.st b/src/Toplo/TToTripletElement.trait.st index 3f3b44680..81c5f6b78 100644 --- a/src/Toplo/TToTripletElement.trait.st +++ b/src/Toplo/TToTripletElement.trait.st @@ -8,22 +8,17 @@ Trait { 'middleElement', 'endElement' ], - #category : #'Toplo-Core' + #category : #'Toplo-Core-Triplet' } { #category : #'t - triplet element - accessing' } -TToTripletElement >> alignCenter [ - ^ self toConfiguration alignCenter -] - -{ #category : #'t - triplet element - accessing' } TToTripletElement >> alignCenter: aBoolean [ - self alignCenter = aBoolean ifTrue: [ ^ self ]. - self toConfiguration alignCenter: aBoolean. - self requestConfiguration. - self requestNewSkin + aBoolean + ifTrue: [ self layout alignCenter ] + ifFalse: [ self layout alignCenterLeft ] + ] { #category : #'t - triplet element - instance creation' } @@ -56,7 +51,7 @@ TToTripletElement >> checkStartContainer [ { #category : #'t - triplet element - instance creation' } TToTripletElement >> createEndContainer [ - endContainer := self newFrameContainer + endContainer := self newLinearContainer id: #'end-container'; yourself. self addChild: endContainer @@ -74,7 +69,7 @@ TToTripletElement >> createMiddleContainer [ { #category : #'t - triplet element - instance creation' } TToTripletElement >> createStartContainer [ - startContainer := self newFrameContainer + startContainer := self newLinearContainer id: #'start-container'; yourself. self addChild: startContainer at: 1 @@ -220,6 +215,8 @@ TToTripletElement >> initializeAsTripletElement [ "the midddle container must be created because other parts are added according to its position" self createMiddleContainer. + self layout: self defaultLayout. + self alignCenter: false. self addEventHandler: self defaultTripletEventHandler ] @@ -277,22 +274,7 @@ TToTripletElement >> newFiller [ ^ BlElement new extent: 0 @ 0; - layout: BlLinearLayout new; - yourself -] - -{ #category : #'t - triplet element - instance creation' } -TToTripletElement >> newFrameContainer [ - - ^ BlElement new - constraintsDo: [ :c | - c horizontal fitContent. - c vertical fitContent. - c linear vertical alignCenter. - c linear horizontal alignCenter. - c frame vertical alignCenter. - c frame horizontal alignCenter ]; - layout: BlFrameLayout new; + layout: BlBasicLayout new; yourself ] @@ -303,10 +285,8 @@ TToTripletElement >> newLinearContainer [ constraintsDo: [ :c | c horizontal fitContent. c vertical fitContent. - c linear vertical alignCenter. - c linear horizontal alignCenter. - c frame vertical alignCenter. - c frame horizontal alignCenter ]; + c triplet vertical alignCenter. + c triplet horizontal alignCenter ]; layout: BlLinearLayout new; yourself ] @@ -314,7 +294,7 @@ TToTripletElement >> newLinearContainer [ { #category : #'t - triplet element - instance creation' } TToTripletElement >> newMiddleContainer [ - ^ self newFrameContainer + ^ self newLinearContainer ] { #category : #'t - triplet element - accessing' } diff --git a/src/Toplo/TToTripletElementConfiguration.trait.st b/src/Toplo/TToTripletElementConfiguration.trait.st index e69ed217e..0f0ba5ba3 100644 --- a/src/Toplo/TToTripletElementConfiguration.trait.st +++ b/src/Toplo/TToTripletElementConfiguration.trait.st @@ -10,24 +10,11 @@ Trait { 'startAlignment', 'endAlignment', 'endInterspace', - 'startInterspace', - 'alignCenter' + 'startInterspace' ], - #category : #'Toplo-Core' + #category : #'Toplo-Core-Triplet' } -{ #category : #'t - triplet element accessing' } -TToTripletElementConfiguration >> alignCenter [ - - ^ alignCenter -] - -{ #category : #'t - triplet element accessing' } -TToTripletElementConfiguration >> alignCenter: aBoolean [ - - alignCenter := aBoolean -] - { #category : #'t - triplet element configuration' } TToTripletElementConfiguration >> applyTripletElementConfigurationOn: anElement [ @@ -52,15 +39,11 @@ TToTripletElementConfiguration >> configureChildrenIn: anElement [ anElement isHorizontal ifTrue: [ allChildren do: [ :child | - child constraints linear vertical alignCenter. - child constraints frame vertical alignCenter ] ] + child constraints triplet vertical alignCenter ] ] ifFalse: [ allChildren do: [ :child | - child constraints linear horizontal alignCenter. - child constraints frame horizontal alignCenter ] ]. - alignCenter - ifTrue: [ anElement layout alignCenter ] - ifFalse: [ anElement layout alignCenterLeft ]. + child constraints triplet horizontal alignCenter ] ]. + anElement requestLayout ] @@ -89,11 +72,9 @@ TToTripletElementConfiguration >> configureEndElementIn: anElement [ anElement endElement ifNil: [ ^ self ]. anElement isHorizontal ifTrue: [ - anElement endElement constraints frame horizontal alignRight. - anElement endElement constraints linear horizontal alignRight ] + anElement endElement constraints linear horizontal alignRight ] ifFalse: [ - anElement endElement constraints frame vertical alignBottom. - anElement endElement constraints linear vertical alignBottom ] + anElement endElement constraints linear vertical alignBottom ] ] { #category : #'t - triplet element configuration' } @@ -101,18 +82,17 @@ TToTripletElementConfiguration >> configureEndFillerIn: anElement [ endFlexible ifTrue: [ - endFiller ifNil: [ + endFiller ifNotNil: [ ^ self ]. endFiller := anElement newFiller - id: #'end-filler'; - yourself. - anElement addChild: endFiller after: anElement middleContainer ]. - self on: endFiller resizerDo: [ :c | - c vertical matchParent. - c horizontal matchParent ] ] + id: #'end-filler'; + yourself. + endFiller constraints vertical matchParent. + endFiller constraints horizontal matchParent. + anElement addChild: endFiller after: anElement middleContainer ] ifFalse: [ - endFiller ifNil: [ ^ self ]. - endFiller removeFromParent. - endFiller := nil ] + endFiller ifNil: [ ^ self ]. + endFiller removeFromParent. + endFiller := nil ] ] { #category : #'t - triplet element configuration' } @@ -165,11 +145,9 @@ TToTripletElementConfiguration >> configureStartElementIn: anElement [ anElement startElement ifNil: [ ^ self ]. anElement isHorizontal ifTrue: [ - anElement startElement constraints frame horizontal alignLeft. - anElement startElement constraints linear horizontal alignLeft ] + anElement startElement constraints linear horizontal alignLeft ] ifFalse: [ - anElement startElement constraints frame vertical alignBottom. - anElement startElement constraints linear vertical alignBottom ] + anElement startElement constraints linear vertical alignBottom ] ] { #category : #'t - triplet element configuration' } @@ -177,18 +155,17 @@ TToTripletElementConfiguration >> configureStartFillerIn: anElement [ startFlexible ifTrue: [ - startFiller ifNil: [ + startFiller ifNotNil: [ ^ self ]. startFiller := anElement newFiller id: #'start-filler'; yourself. - anElement addChild: startFiller before: anElement middleContainer ]. - self on: startFiller resizerDo: [ :c | - c vertical matchParent. - c horizontal matchParent ] ] + startFiller constraints vertical matchParent. + startFiller constraints horizontal matchParent. + anElement addChild: startFiller before: anElement middleContainer ] ifFalse: [ - startFiller ifNil: [ ^ self ]. - startFiller removeFromParent. - startFiller := nil ] + startFiller ifNil: [ ^ self ]. + startFiller removeFromParent. + startFiller := nil ] ] { #category : #'t - triplet element configuration' } @@ -269,7 +246,6 @@ TToTripletElementConfiguration >> endInterspace: aNumber [ { #category : #'t - triplet element configuration' } TToTripletElementConfiguration >> initializeAsTripletElementConfiguration [ - alignCenter := false. startFlexible := false. endFlexible := false. startInterspace := 0. diff --git a/src/Toplo/ToLabeledIcon.class.st b/src/Toplo/ToLabeledIcon.class.st index a3255091f..10fd8fd7f 100644 --- a/src/Toplo/ToLabeledIcon.class.st +++ b/src/Toplo/ToLabeledIcon.class.st @@ -3,5 +3,5 @@ Class { #superclass : #ToTripletElement, #traits : 'TToLabeledIcon', #classTraits : 'TToLabeledIcon classTrait', - #category : #'Toplo-Core' + #category : #'Toplo-Core-Triplet' } diff --git a/src/Toplo/ToTripletElement.class.st b/src/Toplo/ToTripletElement.class.st index c5f620f8f..dff5a9909 100644 --- a/src/Toplo/ToTripletElement.class.st +++ b/src/Toplo/ToTripletElement.class.st @@ -3,13 +3,13 @@ Class { #superclass : #ToElement, #traits : 'TToOrientable + TToTripletElement', #classTraits : 'TToOrientable classTrait + TToTripletElement classTrait', - #category : #'Toplo-Core' + #category : #'Toplo-Core-Triplet' } { #category : #initialization } ToTripletElement >> defaultLayout [ - ^ BlLinearLayout horizontal. + ^ ToTripletLayout horizontal. ] @@ -20,18 +20,6 @@ ToTripletElement >> initialize [ self initializeAsTripletElement ] -{ #category : #skin } -ToTripletElement >> installRawStyle [ - - super installRawStyle. - self childrenDo: [ :child | - child constraintsDo: [ :c | - c linear vertical alignCenter. - c linear horizontal alignCenter ] ]. - - -] - { #category : #configuration } ToTripletElement >> newToConfiguration [ diff --git a/src/Toplo/ToTripletElementConfiguration.class.st b/src/Toplo/ToTripletElementConfiguration.class.st index 59c948f3e..ba2a5a594 100644 --- a/src/Toplo/ToTripletElementConfiguration.class.st +++ b/src/Toplo/ToTripletElementConfiguration.class.st @@ -3,7 +3,7 @@ Class { #superclass : #ToWidgetConfiguration, #traits : 'TToTripletElementConfiguration', #classTraits : 'TToTripletElementConfiguration classTrait', - #category : #'Toplo-Core' + #category : #'Toplo-Core-Triplet' } { #category : #hook } diff --git a/src/Toplo/ToTripletElementEventHandler.class.st b/src/Toplo/ToTripletElementEventHandler.class.st index 7e87da670..da57bd389 100644 --- a/src/Toplo/ToTripletElementEventHandler.class.st +++ b/src/Toplo/ToTripletElementEventHandler.class.st @@ -1,7 +1,7 @@ Class { #name : #ToTripletElementEventHandler, #superclass : #BlCustomEventHandler, - #category : #'Toplo-Core' + #category : #'Toplo-Core-Triplet' } { #category : #'events handling' } diff --git a/src/Toplo/ToTripletLayout.class.st b/src/Toplo/ToTripletLayout.class.st new file mode 100644 index 000000000..700d11aed --- /dev/null +++ b/src/Toplo/ToTripletLayout.class.st @@ -0,0 +1,200 @@ +Class { + #name : #ToTripletLayout, + #superclass : #BlLayout, + #traits : 'TBlAlignable + TBlOrientableLayout', + #classTraits : 'TBlAlignable classTrait + TBlOrientableLayout classTrait', + #instVars : [ + 'verticalAlignment', + 'horizontalAlignment', + 'orientation', + 'direction' + ], + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #constraints } +ToTripletLayout class >> constraints [ + + ^ ToTripletLayoutConstraints new +] + +{ #category : #factory } +ToTripletLayout class >> horizontal [ + ^ self new beHorizontal +] + +{ #category : #factory } +ToTripletLayout class >> vertical [ + ^ self new beVertical +] + +{ #category : #'api - alignment' } +ToTripletLayout >> align: aChildElement horizontal: aBlAlignment [ + aChildElement constraintsDo: [ :c | (c at: self class) horizontal alignment: aBlAlignment ] +] + +{ #category : #'api - alignment' } +ToTripletLayout >> align: aChildElement vertical: aBlAlignment [ + aChildElement constraintsDo: [ :c | (c at: self class) vertical alignment: aBlAlignment ] +] + +{ #category : #accessing } +ToTripletLayout >> alignment [ + ^ (self horizontalAlignment + self verticalAlignment) directed: self direction +] + +{ #category : #orientation } +ToTripletLayout >> beHorizontal [ + "Change my orientation to horizontal" + + self orientation: ToTripletLayoutHorizontalOrientation new +] + +{ #category : #orientation } +ToTripletLayout >> beVertical [ + "Change my orientation to vertical" + + self orientation: ToTripletLayoutVerticalOrientation new +] + +{ #category : #initialization } +ToTripletLayout >> defaultHorizontalAlignment [ + "By default alignment should not influence on layout process, + so to not deal with nils use null object" + + + ^ BlElementAlignment null horizontal +] + +{ #category : #initialization } +ToTripletLayout >> defaultOrientation [ + "Return default orientation used by LinearLayout. If + instance of my class is created with #new a horizontal orientation + is used by defualt" + + + ^ ToTripletLayoutHorizontalOrientation new +] + +{ #category : #initialization } +ToTripletLayout >> defaultVerticalAlignment [ + "By default alignment should not influence on layout process, + so to not deal with nils use null object" + + + ^ BlElementAlignment null vertical +] + +{ #category : #'api - direction' } +ToTripletLayout >> direction [ + + ^ direction ifNil: [ self defaultDirection ] +] + +{ #category : #'api - direction' } +ToTripletLayout >> direction: aBlLayoutDirection [ + + direction := aBlLayoutDirection +] + +{ #category : #accessing } +ToTripletLayout >> horizontalAlignment [ + ^ horizontalAlignment +] + +{ #category : #accessing } +ToTripletLayout >> horizontalAlignment: anAlignment [ + horizontalAlignment := anAlignment +] + +{ #category : #initialization } +ToTripletLayout >> initialize [ + + super initialize. + + verticalAlignment := self defaultVerticalAlignment. + horizontalAlignment := self defaultHorizontalAlignment. + + self orientation: self defaultOrientation +] + +{ #category : #layout } +ToTripletLayout >> layout: anElement in: aRectangle context: aBlElementBoundsUpdateContext [ + + self orientation layout: anElement in: aRectangle context: aBlElementBoundsUpdateContext. + self layoutIgnored: anElement context: aBlElementBoundsUpdateContext +] + +{ #category : #'api - direction' } +ToTripletLayout >> leftToRight [ + + self direction: BlLayoutDirection leftToRight +] + +{ #category : #measure } +ToTripletLayout >> measure: anElement with: anExtentSpec [ + | aMeasurer | + "I measure a new extent of myself and my children" + + aMeasurer := self newMeasurer. + aMeasurer + initializeWith: anElement + layout: self + extentSpec: anExtentSpec + orientation: self orientation. + + aMeasurer measureOn: anElement. + + self + measureIgnored: anElement + with: (BlExtentMeasurementSpec exact: anElement measuredExtent) +] + +{ #category : #measure } +ToTripletLayout >> newHorizontalMeasurer [ + + ^ ToTripletLayoutHorizontalMeasurer new +] + +{ #category : #measure } +ToTripletLayout >> newMeasurer [ + + ^ self orientation isHorizontal + ifTrue: [ self newHorizontalMeasurer ] + ifFalse: [ self newVerticalMeasurer ] +] + +{ #category : #measure } +ToTripletLayout >> newVerticalMeasurer [ + + ^ ToTripletLayoutVerticalMeasurer new +] + +{ #category : #accessing } +ToTripletLayout >> orientation [ + ^ orientation +] + +{ #category : #accessing } +ToTripletLayout >> orientation: anOrientation [ + + anOrientation layout: self. + orientation := anOrientation. + +] + +{ #category : #'api - direction' } +ToTripletLayout >> rightToLeft [ + + self direction: BlLayoutDirection rightToLeft +] + +{ #category : #accessing } +ToTripletLayout >> verticalAlignment [ + ^ verticalAlignment +] + +{ #category : #accessing } +ToTripletLayout >> verticalAlignment: anAlignment [ + verticalAlignment := anAlignment +] diff --git a/src/Toplo/ToTripletLayoutConstraints.class.st b/src/Toplo/ToTripletLayoutConstraints.class.st new file mode 100644 index 000000000..1bd44ffa4 --- /dev/null +++ b/src/Toplo/ToTripletLayoutConstraints.class.st @@ -0,0 +1,56 @@ +Class { + #name : #ToTripletLayoutConstraints, + #superclass : #BlLayoutConstraints, + #instVars : [ + 'weight' + ], + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #alignment } +ToTripletLayoutConstraints >> alignment [ + ^ self horizontal alignment + self vertical alignment +] + +{ #category : #initialization } +ToTripletLayoutConstraints >> defaultHorizontal [ + ^ ToTripletLayoutConstraintsHorizontal new +] + +{ #category : #initialization } +ToTripletLayoutConstraints >> defaultVertical [ + ^ ToTripletLayoutConstraintsVertical new +] + +{ #category : #initialization } +ToTripletLayoutConstraints >> initialize [ + super initialize. + + weight := 1 +] + +{ #category : #weight } +ToTripletLayoutConstraints >> weight [ + "Return weight that defines how much space an element takes within parent's available space. + weight can not be nil and is always non negative" + + + ^ weight +] + +{ #category : #weight } +ToTripletLayoutConstraints >> weight: aNumber [ + "Set new weight value that defines how much space an element takes within parent's available space. + aNumber must be non negative + aNumber must not be infinite" + + self + assert: [ aNumber >= 0 ] + description: [ 'Weight must be non negative' ]. + + self + assert: [ aNumber < Float infinity ] + description: [ 'Weight must not be infinite' ]. + + weight := aNumber +] diff --git a/src/Toplo/ToTripletLayoutConstraintsHorizontal.class.st b/src/Toplo/ToTripletLayoutConstraintsHorizontal.class.st new file mode 100644 index 000000000..a5baa333e --- /dev/null +++ b/src/Toplo/ToTripletLayoutConstraintsHorizontal.class.st @@ -0,0 +1,40 @@ +Class { + #name : #ToTripletLayoutConstraintsHorizontal, + #superclass : #BlLayoutConstraintsAxis, + #instVars : [ + 'alignment' + ], + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #alignment } +ToTripletLayoutConstraintsHorizontal >> alignCenter [ + alignment := BlElementAlignment horizontal center +] + +{ #category : #alignment } +ToTripletLayoutConstraintsHorizontal >> alignLeft [ + alignment := BlElementAlignment horizontal start +] + +{ #category : #alignment } +ToTripletLayoutConstraintsHorizontal >> alignRight [ + alignment := BlElementAlignment horizontal end +] + +{ #category : #accessing } +ToTripletLayoutConstraintsHorizontal >> alignment [ + ^ alignment +] + +{ #category : #accessing } +ToTripletLayoutConstraintsHorizontal >> alignment: aBlElementAlignment [ + alignment := aBlElementAlignment +] + +{ #category : #initialization } +ToTripletLayoutConstraintsHorizontal >> initialize [ + super initialize. + + alignment := BlElementAlignment horizontal start +] diff --git a/src/Toplo/ToTripletLayoutConstraintsVertical.class.st b/src/Toplo/ToTripletLayoutConstraintsVertical.class.st new file mode 100644 index 000000000..11c8dfc8f --- /dev/null +++ b/src/Toplo/ToTripletLayoutConstraintsVertical.class.st @@ -0,0 +1,40 @@ +Class { + #name : #ToTripletLayoutConstraintsVertical, + #superclass : #BlLayoutConstraintsAxis, + #instVars : [ + 'alignment' + ], + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #alignment } +ToTripletLayoutConstraintsVertical >> alignBottom [ + alignment := BlElementAlignment vertical end +] + +{ #category : #alignment } +ToTripletLayoutConstraintsVertical >> alignCenter [ + alignment := BlElementAlignment vertical center +] + +{ #category : #alignment } +ToTripletLayoutConstraintsVertical >> alignTop [ + alignment := BlElementAlignment vertical start +] + +{ #category : #accessing } +ToTripletLayoutConstraintsVertical >> alignment [ + ^ alignment +] + +{ #category : #accessing } +ToTripletLayoutConstraintsVertical >> alignment: aBlElementAlignment [ + alignment := aBlElementAlignment +] + +{ #category : #initialization } +ToTripletLayoutConstraintsVertical >> initialize [ + super initialize. + + alignment := BlElementAlignment vertical start +] diff --git a/src/Toplo/ToTripletLayoutHorizontalMeasurer.class.st b/src/Toplo/ToTripletLayoutHorizontalMeasurer.class.st new file mode 100644 index 000000000..d7fed2ca2 --- /dev/null +++ b/src/Toplo/ToTripletLayoutHorizontalMeasurer.class.st @@ -0,0 +1,26 @@ +Class { + #name : #ToTripletLayoutHorizontalMeasurer, + #superclass : #ToTripletLayoutMeasurer, + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #'private - measurement' } +ToTripletLayoutHorizontalMeasurer >> measureExactUsingExcessSpace: aNode [ + "Optimization: don't bother measuring children who are only laid out using excess space. + These views will get measured later if we have space to distribute." + + totalLength := self isLengthExact + ifTrue: [ totalLength + aNode marginLength ] + ifFalse: [ totalLength max: (totalLength + aNode marginLength) ] +] + +{ #category : #'private - measurement' } +ToTripletLayoutHorizontalMeasurer >> updateTotalLengthAfterMeasuring: aNode [ + "I update the total length after measuring a given node based on its measured length and margin" + + self flag: 'Why accounting for isBreadthExact is needed for the horizontal layout?'. + + totalLength := self isLengthExact + ifTrue: [ totalLength + aNode measuredLength + aNode marginLength ] + ifFalse: [ totalLength max: (totalLength + aNode measuredLength + aNode marginLength) ] +] diff --git a/src/Toplo/ToTripletLayoutHorizontalOrientation.class.st b/src/Toplo/ToTripletLayoutHorizontalOrientation.class.st new file mode 100644 index 000000000..c319fc38d --- /dev/null +++ b/src/Toplo/ToTripletLayoutHorizontalOrientation.class.st @@ -0,0 +1,180 @@ +Class { + #name : #ToTripletLayoutHorizontalOrientation, + #superclass : #ToTripletLayoutOrientation, + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> breadthProperties: anElement [ + ^ anElement constraints vertical +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> breadthSpec: anExtentSpec [ + ^ anExtentSpec heightSpec +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> extentBreadth: breadth lengthSpec: length [ + ^ length @ breadth +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> extentSpecBreadth: breadthSpec lengthSpec: lengthSpec [ + + ^ BlExtentMeasurementSpec + widthSpec: lengthSpec + heightSpec: breadthSpec +] + +{ #category : #factory } +ToTripletLayoutHorizontalOrientation >> inverted [ + + ^ ToTripletLayoutVerticalOrientation new +] + +{ #category : #testing } +ToTripletLayoutHorizontalOrientation >> isHorizontal [ + ^ true +] + +{ #category : #layout } +ToTripletLayoutHorizontalOrientation >> layout: anElement in: aRectangle context: aBlElementBoundsUpdateContext [ + + | top right bottom left majorBounds elementInnerBounds theLayeredChildren | + "Layout subnodes vertically in column one by one + based on previousely measured extent" + top := anElement padding top. + left := anElement padding left. + right := left. + bottom := top. + + theLayeredChildren := anElement children accountedByLayout. + + self layout direction with: theLayeredChildren do: [ :child | + right := right + child measuredWidth + + (child margin width max: 0.0). + bottom := bottom max: + child measuredHeight + (child margin height max: 0.0) ]. + + majorBounds := left @ top corner: right @ bottom. + elementInnerBounds := anElement padding inset: + anElement boundsInLocal. + majorBounds = elementInnerBounds ifFalse: [ + | translation | + translation := (self layout horizontalAlignment directed: + self layout direction) + translationOf: majorBounds + in: elementInnerBounds. + majorBounds := majorBounds translateBy: translation ]. + + right := majorBounds right. + bottom := majorBounds bottom. + left := majorBounds left. + top := majorBounds top. + + self layout direction + with: theLayeredChildren + inject: left @ top + into: [ :origin :child | + | childBounds childMarginBounds childConstraints | + childConstraints := child constraints triplet. + childBounds := origin + (child margin topLeft max: 0.0 @ 0.0) + extent: child measuredExtent. + childMarginBounds := origin extent: + child measuredExtent + + (child margin extent max: 0.0 @ 0.0). + + self layout verticalAlignment ifNotNull: [ + | translation allowedBounds | + allowedBounds := childMarginBounds origin extent: + child measuredWidth + @ elementInnerBounds height. + translation := self layout verticalAlignment + translationOf: childMarginBounds + in: allowedBounds. + childBounds := childBounds translateBy: translation ]. + + self layout verticalAlignment ifNull: [ + | translation allowedBounds | + allowedBounds := childMarginBounds origin extent: + child measuredWidth + @ elementInnerBounds height. + translation := childConstraints vertical alignment + translationOf: childMarginBounds + in: allowedBounds. + childBounds := childBounds translateBy: translation ]. + + "telling each subnode what bounds to use for layouting process. + Because measuring process does not modify actual extent + we need to use a measured one" + child + applyLayoutIn: childBounds + context: aBlElementBoundsUpdateContext. + "translating origin horizontally right for next subnode" + origin x + child measuredWidth + (child margin width max: 0.0) + @ origin y ] +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> lengthProperties: aChildElement [ + ^ aChildElement constraints horizontal +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> lengthSpec: anExtentSpec [ + ^ anExtentSpec widthSpec +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> marginBreadth: anElement [ + + ^ anElement margin height +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> marginLength: anElement [ + + ^ anElement margin width +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> measureChildWithMargin: child parentSpec: anExtentMeasurementSpec breadthUsed: breadthUsed lengthUsed: lengthUsed [ + self layout + measureChildWithMargins: child + parentSpec: anExtentMeasurementSpec + widthUsed: lengthUsed + heightUsed: breadthUsed +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> measuredBreadth: anElement [ + ^ anElement measuredHeight +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> measuredLength: anElement [ + ^ anElement measuredWidth +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> minimalBreadth: anElement [ + ^ anElement constraints minHeight +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> minimalLength: anElement [ + ^ anElement constraints minWidth +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> paddingBreadth: anElement [ + + ^ anElement padding height +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> paddingLength: anElement [ + + ^ anElement padding width +] diff --git a/src/Toplo/ToTripletLayoutMeasurer.class.st b/src/Toplo/ToTripletLayoutMeasurer.class.st new file mode 100644 index 000000000..f154fdad9 --- /dev/null +++ b/src/Toplo/ToTripletLayoutMeasurer.class.st @@ -0,0 +1,360 @@ +Class { + #name : #ToTripletLayoutMeasurer, + #superclass : #Object, + #instVars : [ + 'nodes', + 'parent', + 'breadthSpec', + 'lengthSpec', + 'orientation', + 'totalLength', + 'totalWeight', + 'extentSpec', + 'usedExcessSpace', + 'matchBreadth', + 'maxBreadth', + 'measuredLength', + 'layout' + ], + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #accessing } +ToTripletLayoutMeasurer >> breadthSpec [ + ^ breadthSpec +] + +{ #category : #measurement } +ToTripletLayoutMeasurer >> canMeasureExactUsingExcessSpaceForNode: aNode [ + + ^ self isLengthExact and: [ self shouldUseExcessSpaceForNode: aNode ] +] + +{ #category : #'private - measurement' } +ToTripletLayoutMeasurer >> childBreadthSpecForNode: aNode [ + + ^ parent layout + measurementSpecFor: breadthSpec + usedSize: + (orientation paddingBreadth: parent) + aNode marginBreadth + resizer: aNode breadthResizer +] + +{ #category : #initialization } +ToTripletLayoutMeasurer >> initialize [ + super initialize. + + totalLength := 0.0. + totalWeight := 0.0. + usedExcessSpace := 0.0. + matchBreadth := false. + measuredLength := 0.0. + maxBreadth := 0.0 +] + +{ #category : #initialization } +ToTripletLayoutMeasurer >> initializeWith: aParentElement layout: aLinearLayout extentSpec: anExtentSpec orientation: aToTripletLayoutOrientation [ + + parent := aParentElement. + orientation := aToTripletLayoutOrientation. + layout := aLinearLayout. + + extentSpec := anExtentSpec. + breadthSpec := orientation breadthSpec: anExtentSpec. + lengthSpec := orientation lengthSpec: anExtentSpec. + + nodes := aParentElement children accountedByLayout collect: [ + :aChildElement | + ToTripletLayoutNode new + initializeWith: aChildElement + orientation: orientation ] +] + +{ #category : #testing } +ToTripletLayoutMeasurer >> isBreadthExact [ + ^ self breadthSpec isExact +] + +{ #category : #testing } +ToTripletLayoutMeasurer >> isLengthExact [ + ^ self lengthSpec isExact +] + +{ #category : #accessing } +ToTripletLayoutMeasurer >> layout [ + + ^ layout +] + +{ #category : #accessing } +ToTripletLayoutMeasurer >> lengthSpec [ + ^ lengthSpec +] + +{ #category : #measurement } +ToTripletLayoutMeasurer >> limitedMeasure [ + | excessSpace remainingExcess remainingNodesCount theLimitedNodes totalLimitedLength maxEachLimitedLength theNodesToShrink totalShrinkedLength | + + "Either expand children with weight to take up available space or shrink them if they extend beyond our current bounds. + If we skipped measurement on any children, we need to measure them now." + excessSpace := measuredLength - totalLength + usedExcessSpace. + excessSpace >= 0 + ifTrue: [ ^ self ]. + + excessSpace := excessSpace abs. + remainingExcess := excessSpace. + + "we should reduce the size of the fit content limited children" + theLimitedNodes := self nodes select: [ :eachNode | eachNode lengthResizer isFitContentLimited ]. + theLimitedNodes + ifEmpty: [ ^ self ]. + + totalLimitedLength := (theLimitedNodes inject: 0.0 into: [ :aSum :eachNode | aSum + eachNode measuredLength + eachNode marginLength ]) - remainingExcess. + totalLimitedLength < 0 + ifTrue: [ ^ self ]. + + maxEachLimitedLength := totalLimitedLength / theLimitedNodes size. + + "let's select those nodes that are larger than the max length and need to be schinked" + theNodesToShrink := theLimitedNodes select: [ :eachNode | eachNode measuredLength > maxEachLimitedLength ]. + theNodesToShrink + ifEmpty: [ ^ self ]. + + totalShrinkedLength := (theNodesToShrink inject: 0.0 into: [ :aSum :eachNode | aSum + eachNode measuredLength + eachNode marginLength ]) - remainingExcess. + totalShrinkedLength < 0 + ifTrue: [ ^ self ]. + + remainingNodesCount := theNodesToShrink size. + theNodesToShrink do: [ :eachNode | + | shareLength childLength childLengthSpec childBreadthSpec | + + shareLength := remainingExcess / remainingNodesCount. + remainingExcess := remainingExcess - shareLength. + remainingNodesCount := remainingNodesCount - 1. + + childLength := eachNode measuredLength - shareLength. + + childLengthSpec := BlMeasurementSpec exact: (0.0 max: childLength). + childBreadthSpec := self childBreadthSpecForNode: eachNode. + + eachNode measure: (orientation extentSpecBreadth: childBreadthSpec lengthSpec: childLengthSpec) ]. + + measuredLength := measuredLength - excessSpace. + totalLength := totalLength - excessSpace. +] + +{ #category : #'private - measurement' } +ToTripletLayoutMeasurer >> measureExactUsingExcessSpace: aNode [ + "Optimization: don't bother measuring children who are only laid out using excess space. + These views will get measured later if we have space to distribute." + + self subclassResponsibility +] + +{ #category : #measurement } +ToTripletLayoutMeasurer >> measureOn: anElement [ + + anElement ignoreRequestLayoutDuring: [ self preMeasure ]. + + totalLength := totalLength + (orientation paddingLength: parent). + + "Check against our minimum length" + measuredLength := totalLength max: (orientation minimalLength: parent). + + "Reconcile our calculated size with the lengthSpec" + measuredLength := lengthSpec sizeFor: measuredLength. + + self limitedMeasure. + self weightedMeasure. + self postMeasure. + self uniformMeasure +] + +{ #category : #accessing } +ToTripletLayoutMeasurer >> nodes [ + + + ^ nodes +] + +{ #category : #accessing } +ToTripletLayoutMeasurer >> parent [ + + + ^ parent +] + +{ #category : #measurement } +ToTripletLayoutMeasurer >> postMeasure [ + + maxBreadth := maxBreadth + (orientation paddingBreadth: parent). + + "Check against our minimum height" + maxBreadth := maxBreadth max: (orientation minimalBreadth: parent). + + parent measuredExtent: + (orientation + extentBreadth: (breadthSpec sizeFor: maxBreadth) + lengthSpec: measuredLength) +] + +{ #category : #measurement } +ToTripletLayoutMeasurer >> preMeasure [ + + "See how wide/tall everyone is. Also remember max breadth." + + self nodes do: [ :aNode | + | matchBreadthLocally childBreadth shouldUseExcessSpace | + + totalWeight := totalWeight + aNode weight. + + "we must store the result because it may temporary change during measurement" + shouldUseExcessSpace := self shouldUseExcessSpaceForNode: aNode. + + (self canMeasureExactUsingExcessSpaceForNode: aNode) + ifTrue: [ self measureExactUsingExcessSpace: aNode ] + ifFalse: [ + | usedLength childLength | + "The lengthMode is either UNSPECIFIED or AT_MOST, and + this child is only laid out using excess space. + Measure using FIT_CONTENT so that we can find out the view's optimal length. + We'll restore the original length of 0 after measurement." + shouldUseExcessSpace + ifTrue: [ aNode lengthResizer: BlLayoutResizer fitContent ]. + + "Determine how big this child would like to be. + If this or previous children have given a weight, + then we allow it to use all available space (and we will shrink things later if needed)." + usedLength := totalWeight isZero + ifTrue: [ totalLength ] + ifFalse: [ 0 ]. + orientation + measureChildWithMargin: aNode element + parentSpec: extentSpec + breadthUsed: 0 + lengthUsed: usedLength. + + childLength := aNode measuredLength. + shouldUseExcessSpace + ifTrue: [ + "Restore the original width and record how much space + we've allocated to excess-only children so that we can + match the behavior of EXACTLY measurement." + aNode lengthResizer: BlLayoutResizer matchParent. + usedExcessSpace := usedExcessSpace + childLength ]. + + self updateTotalLengthAfterMeasuring: aNode ]. + + matchBreadthLocally := false. + (self isBreadthExact not and: [ aNode breadthResizer isMatchParent ]) + ifTrue: [ + "The height of the linear layout will scale, and at least one child said it wanted to match our height. + Set a flag indicating that we need to remeasure at least that view when we know our height." + matchBreadth := true. + matchBreadthLocally := true ]. + + childBreadth := aNode measuredBreadth + aNode marginBreadth. + + maxBreadth := maxBreadth max: childBreadth ] +] + +{ #category : #'private - measurement' } +ToTripletLayoutMeasurer >> prepareForWeightedMeasure [ + + totalLength := 0.0. +] + +{ #category : #'accessing - properties' } +ToTripletLayoutMeasurer >> remainingWeightSum [ + + ^ totalWeight +] + +{ #category : #'private - measurement' } +ToTripletLayoutMeasurer >> shouldUseExcessSpaceForNode: aNode [ + + ^ (lengthSpec isUnspecified not) and: [ + aNode shouldUseExcessSpace ] +] + +{ #category : #measurement } +ToTripletLayoutMeasurer >> uniformMeasure [ + | uniformMeasureSpec | + + matchBreadth + ifFalse: [ ^ self ]. + + "Pretend that the linear layout has an exact size. This is the measured height of ourselves. + The measured height should be the max height of the children, changed to accommodate the heightMeasureSpec from the parent" + + uniformMeasureSpec := BlMeasurementSpec exact: (orientation measuredBreadth: parent). + + self nodes + select: [ :aNode | aNode breadthResizer isMatchParent ] + thenDo: [ :aNode | + | oldResizer | + "Temporarily force children to reuse their old measured width" + oldResizer := aNode lengthResizer. + aNode lengthResizer: (BlLayoutResizer exact: aNode measuredLength). + + "Remeasure with new dimensions" + orientation + measureChildWithMargin: aNode element + parentSpec: (orientation + extentSpecBreadth: uniformMeasureSpec + lengthSpec: self lengthSpec) + breadthUsed: 0 + lengthUsed: 0. + + aNode lengthResizer: oldResizer ]. +] + +{ #category : #'private - measurement' } +ToTripletLayoutMeasurer >> updateTotalLengthAfterMeasuring: aNode [ + "I update the total length after measuring a given node based on its measured length and margin" + + self subclassResponsibility +] + +{ #category : #measurement } +ToTripletLayoutMeasurer >> weightedMeasure [ + + | remainingExcess remainingWeightSum | + measuredLength := totalLength max: + (orientation minimalLength: parent). + measuredLength := lengthSpec sizeFor: measuredLength. + remainingExcess := measuredLength - totalLength + usedExcessSpace. + + (remainingExcess > 0 and: [ totalWeight > 0 ]) ifFalse: [ ^ self ]. + remainingWeightSum := self remainingWeightSum. + + self prepareForWeightedMeasure. + + self nodes do: [ :aNode | + | childWeight childLength childBreadth matchBreadthLocally | + childWeight := aNode weight. + childWeight > 0 ifTrue: [ + | shareSpace childLengthSpec childBreadthSpec | + shareSpace := (remainingWeightSum closeTo: 0) + ifTrue: [ 0.0 ] + ifFalse: [ + (childWeight * remainingExcess + / remainingWeightSum) asFloat ]. + remainingExcess := remainingExcess - shareSpace. + remainingWeightSum := remainingWeightSum - childWeight. + childLength := aNode lengthResizer isMatchParent + ifTrue: [ shareSpace ] + ifFalse: [ aNode measuredLength + shareSpace ]. + childLengthSpec := BlMeasurementSpec exact: + (0.0 max: childLength). + childBreadthSpec := self childBreadthSpecForNode: aNode. + aNode measure: (orientation + extentSpecBreadth: childBreadthSpec + lengthSpec: childLengthSpec) ]. + self updateTotalLengthAfterMeasuring: aNode. + matchBreadthLocally := breadthSpec isExact not and: [ + aNode breadthResizer isMatchParent ]. + childBreadth := aNode measuredBreadth + aNode marginBreadth. + maxBreadth := maxBreadth max: childBreadth ]. + totalLength := totalLength + (orientation paddingLength: parent) +] diff --git a/src/Toplo/ToTripletLayoutNode.class.st b/src/Toplo/ToTripletLayoutNode.class.st new file mode 100644 index 000000000..20714ddb0 --- /dev/null +++ b/src/Toplo/ToTripletLayoutNode.class.st @@ -0,0 +1,135 @@ +Class { + #name : #ToTripletLayoutNode, + #superclass : #Object, + #instVars : [ + 'element', + 'orientation', + 'linearConstraints' + ], + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #'accessing - constraints' } +ToTripletLayoutNode >> breadthConstraints [ + ^ self orientation breadthProperties: self element +] + +{ #category : #'accessing - properties' } +ToTripletLayoutNode >> breadthResizer [ + ^ self breadthConstraints resizer +] + +{ #category : #accessing } +ToTripletLayoutNode >> constraints [ + ^ self element constraints +] + +{ #category : #accessing } +ToTripletLayoutNode >> element [ + + + ^ element +] + +{ #category : #testing } +ToTripletLayoutNode >> hasSpaceAfter [ + "Return true if there should be a divider after this node" + + + ^ true +] + +{ #category : #testing } +ToTripletLayoutNode >> hasSpaceBefore [ + "Return true if there should be a divider before this node" + + + ^ true +] + +{ #category : #initialization } +ToTripletLayoutNode >> initializeWith: anElement orientation: aToTripletLayoutOrientation [ + element := anElement. + orientation := aToTripletLayoutOrientation. + linearConstraints := anElement constraints triplet +] + +{ #category : #'accessing - constraints' } +ToTripletLayoutNode >> lengthConstraints [ + ^ self orientation lengthProperties: self element +] + +{ #category : #'accessing - properties' } +ToTripletLayoutNode >> lengthResizer [ + ^ self lengthConstraints resizer +] + +{ #category : #'accessing - properties' } +ToTripletLayoutNode >> lengthResizer: aResizer [ + self lengthConstraints resizer: aResizer +] + +{ #category : #accessing } +ToTripletLayoutNode >> linearConstraints [ + + + ^ linearConstraints +] + +{ #category : #'accessing - properties' } +ToTripletLayoutNode >> marginBreadth [ + + + ^ self orientation marginBreadth: self element +] + +{ #category : #'accessing - properties' } +ToTripletLayoutNode >> marginLength [ + + + ^ self orientation marginLength: self element +] + +{ #category : #measure } +ToTripletLayoutNode >> measure: anExtentSpecOrBlock [ + self element measure: anExtentSpecOrBlock value +] + +{ #category : #'accessing - properties' } +ToTripletLayoutNode >> measuredBreadth [ + ^ self orientation measuredBreadth: self element +] + +{ #category : #'accessing - properties' } +ToTripletLayoutNode >> measuredLength [ + ^ self orientation measuredLength: self element +] + +{ #category : #accessing } +ToTripletLayoutNode >> orientation [ + + + ^ orientation +] + +{ #category : #testing } +ToTripletLayoutNode >> shouldUseExcessSpace [ + + + ^ self weight > 0 +] + +{ #category : #'accessing - properties' } +ToTripletLayoutNode >> verticalAlignment [ + ^ self linearConstraints vertical alignment +] + +{ #category : #'accessing - properties' } +ToTripletLayoutNode >> weight [ + "Weight is only applicable if a child matches parent" + + + ^ self lengthResizer isMatchParent + ifTrue: [ self linearConstraints weight ] + ifFalse: [ 0 ] +] diff --git a/src/Toplo/ToTripletLayoutOrientation.class.st b/src/Toplo/ToTripletLayoutOrientation.class.st new file mode 100644 index 000000000..d9e2d4fb2 --- /dev/null +++ b/src/Toplo/ToTripletLayoutOrientation.class.st @@ -0,0 +1,138 @@ +Class { + #name : #ToTripletLayoutOrientation, + #superclass : #Object, + #instVars : [ + 'layout' + ], + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #factory } +ToTripletLayoutOrientation class >> horizontal [ + + ^ ToTripletLayoutHorizontalOrientation new +] + +{ #category : #factory } +ToTripletLayoutOrientation class >> vertical [ + + ^ ToTripletLayoutVerticalOrientation new +] + +{ #category : #comparing } +ToTripletLayoutOrientation >> = anObject [ + self == anObject + ifTrue: [ ^ true ]. + ^ self class = anObject class +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> breadthProperties: anElement [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> breadthSpec: anExtentSpec [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> extentBreadth: breadth lengthSpec: length [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> extentSpecBreadth: breadthSpec lengthSpec: lengthSpec [ + ^ self subclassResponsibility +] + +{ #category : #comparing } +ToTripletLayoutOrientation >> hash [ + ^ self class hash +] + +{ #category : #accessing } +ToTripletLayoutOrientation >> inverted [ + + ^ self subclassResponsibility +] + +{ #category : #testing } +ToTripletLayoutOrientation >> isHorizontal [ + ^ false +] + +{ #category : #testing } +ToTripletLayoutOrientation >> isVertical [ + ^ false +] + +{ #category : #accessing } +ToTripletLayoutOrientation >> layout [ + ^ layout +] + +{ #category : #accessing } +ToTripletLayoutOrientation >> layout: aLinearLayout [ + layout := aLinearLayout +] + +{ #category : #layout } +ToTripletLayoutOrientation >> layout: anElement in: aRectangle context: aBlElementBoundsUpdateContext [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> lengthProperties: anElement [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> lengthSpec: anExtentSpec [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> marginBreadth: anElement [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> marginLength: anElement [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> measureChildWithMargin: child parentSpec: anExtentMeasurementSpec breadthUsed: breadthUsed lengthUsed: lengthUsed [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> measuredBreadth: anElement [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> measuredLength: anElement [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> minimalBreadth: anElement [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> minimalLength: anElement [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> paddingBreadth: anElement [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> paddingLength: anElement [ + ^ self subclassResponsibility +] diff --git a/src/Toplo/ToTripletLayoutVerticalMeasurer.class.st b/src/Toplo/ToTripletLayoutVerticalMeasurer.class.st new file mode 100644 index 000000000..dd56b7c5a --- /dev/null +++ b/src/Toplo/ToTripletLayoutVerticalMeasurer.class.st @@ -0,0 +1,20 @@ +Class { + #name : #ToTripletLayoutVerticalMeasurer, + #superclass : #ToTripletLayoutMeasurer, + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #'private - measurement' } +ToTripletLayoutVerticalMeasurer >> measureExactUsingExcessSpace: aNode [ + "Optimization: don't bother measuring children who are only laid out using excess space. + These views will get measured later if we have space to distribute." + + totalLength := totalLength max: (totalLength + aNode marginLength) +] + +{ #category : #'private - measurement' } +ToTripletLayoutVerticalMeasurer >> updateTotalLengthAfterMeasuring: aNode [ + "I update the total length after measuring a given node based on its measured length and margin" + + totalLength := totalLength max: (totalLength + aNode measuredLength + aNode marginLength) +] diff --git a/src/Toplo/ToTripletLayoutVerticalOrientation.class.st b/src/Toplo/ToTripletLayoutVerticalOrientation.class.st new file mode 100644 index 000000000..4c8a27325 --- /dev/null +++ b/src/Toplo/ToTripletLayoutVerticalOrientation.class.st @@ -0,0 +1,162 @@ +Class { + #name : #ToTripletLayoutVerticalOrientation, + #superclass : #ToTripletLayoutOrientation, + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> breadthProperties: anElement [ + ^ anElement constraints horizontal +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> breadthSpec: anExtentSpec [ + ^ anExtentSpec widthSpec +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> extentBreadth: breadth lengthSpec: length [ + ^ breadth @ length +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> extentSpecBreadth: breadthSpec lengthSpec: lengthSpec [ + + ^ BlExtentMeasurementSpec + widthSpec: breadthSpec + heightSpec: lengthSpec +] + +{ #category : #factory } +ToTripletLayoutVerticalOrientation >> inverted [ + + ^ ToTripletLayoutHorizontalOrientation new +] + +{ #category : #testing } +ToTripletLayoutVerticalOrientation >> isVertical [ + ^ true +] + +{ #category : #layout } +ToTripletLayoutVerticalOrientation >> layout: anElement in: aRectangle context: aBlElementBoundsUpdateContext [ + |top right bottom left majorBounds elementInnerBounds theLayeredChildren | + "Layout subnodes vertically in column one by one + based on previously measured extent" + + top := anElement padding top. + left := anElement padding left. + right := left. + bottom := top. + + theLayeredChildren := anElement children accountedByLayout. + + self layout direction + with: theLayeredChildren + do: [ :child | + right := right max: child measuredWidth + (child margin width max: 0.0). + bottom := bottom + child measuredHeight + (child margin height max: 0.0) ]. + + majorBounds := (left@top corner: right@bottom). + elementInnerBounds := anElement padding inset: anElement boundsInLocal. + majorBounds = elementInnerBounds ifFalse: [ + | translation | + translation := (self layout verticalAlignment directed: self layout direction) translationOf: majorBounds in: elementInnerBounds. + majorBounds := majorBounds translateBy: translation ]. + + right := majorBounds right. + bottom := majorBounds bottom. + left := majorBounds left. + top := majorBounds top. + + self layout direction + with: anElement children accountedByLayout + inject: left @ top + into: [ :origin :child | + | childBounds childMarginBounds childConstraints | + childConstraints := child constraints triplet. + childBounds := origin + (child margin topLeft max: 0.0@0.0) extent: child measuredExtent. + childMarginBounds := origin extent: child measuredExtent + (child margin extent max: 0.0@0.0). + + self layout horizontalAlignment ifNotNull: [ + | translation allowedBounds | + allowedBounds := childMarginBounds origin extent: elementInnerBounds width @ child measuredHeight. + translation := (self layout horizontalAlignment directed: self layout direction) translationOf: childMarginBounds in: allowedBounds. + childBounds := childBounds translateBy: translation ]. + + self layout horizontalAlignment ifNull: [ + | translation allowedBounds | + allowedBounds := childMarginBounds origin extent: elementInnerBounds width @ child measuredHeight. + translation := (childConstraints horizontal alignment directed: self layout direction) translationOf: childMarginBounds in: allowedBounds. + childBounds := childBounds translateBy: translation ]. + + "telling each subnode what bounds to use for layouting process. + Because measuring process does not modify actual extent + we need to use a measured one" + child applyLayoutIn: childBounds context: aBlElementBoundsUpdateContext. + "translating origin vertically down for next subnode" + origin x @(origin y + child measuredHeight + (child margin height max: 0.0)) ] +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> lengthProperties: aChildElement [ + ^ aChildElement constraints vertical +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> lengthSpec: anExtentSpec [ + ^ anExtentSpec heightSpec +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> marginBreadth: anElement [ + + ^ anElement margin width +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> marginLength: anElement [ + + ^ anElement margin height +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> measureChildWithMargin: child parentSpec: anExtentMeasurementSpec breadthUsed: breadthUsed lengthUsed: lengthUsed [ + self layout + measureChildWithMargins: child + parentSpec: anExtentMeasurementSpec + widthUsed: breadthUsed + heightUsed: lengthUsed +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> measuredBreadth: anElement [ + ^ anElement measuredWidth +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> measuredLength: anElement [ + ^ anElement measuredHeight +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> minimalBreadth: anElement [ + ^ anElement constraints minWidth +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> minimalLength: anElement [ + ^ anElement constraints minHeight +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> paddingBreadth: anElement [ + + ^ anElement padding width +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> paddingLength: anElement [ + + ^ anElement padding height +]