From 1107abaf10ffa3dcb26d951e01253e6c11285fc4 Mon Sep 17 00:00:00 2001 From: Alain Plantec Date: Sat, 19 Jul 2025 12:11:58 +0200 Subject: [PATCH 1/4] introduce ToTripletLayout starting from a duplication of BlLinearLayout --- src/Toplo-Examples/ToSandBox.class.st | 4 +- .../BlLayoutCommonConstraints.extension.st | 6 + src/Toplo/TToTripletElement.trait.st | 3 +- .../TToTripletElementConfiguration.trait.st | 10 +- src/Toplo/ToLabeledIcon.class.st | 2 +- src/Toplo/ToTripletElement.class.st | 4 +- .../ToTripletElementConfiguration.class.st | 2 +- .../ToTripletElementEventHandler.class.st | 2 +- src/Toplo/ToTripletLayout.class.st | 279 ++++++++++ src/Toplo/ToTripletLayoutConstraints.class.st | 56 ++ ...ripletLayoutConstraintsHorizontal.class.st | 40 ++ ...oTripletLayoutConstraintsVertical.class.st | 40 ++ ...ToTripletLayoutHorizontalMeasurer.class.st | 100 ++++ ...ripletLayoutHorizontalOrientation.class.st | 185 +++++++ src/Toplo/ToTripletLayoutMeasurer.class.st | 510 ++++++++++++++++++ src/Toplo/ToTripletLayoutNode.class.st | 135 +++++ src/Toplo/ToTripletLayoutOrientation.class.st | 153 ++++++ .../ToTripletLayoutVerticalMeasurer.class.st | 41 ++ ...oTripletLayoutVerticalOrientation.class.st | 186 +++++++ 19 files changed, 1745 insertions(+), 13 deletions(-) create mode 100644 src/Toplo/ToTripletLayout.class.st create mode 100644 src/Toplo/ToTripletLayoutConstraints.class.st create mode 100644 src/Toplo/ToTripletLayoutConstraintsHorizontal.class.st create mode 100644 src/Toplo/ToTripletLayoutConstraintsVertical.class.st create mode 100644 src/Toplo/ToTripletLayoutHorizontalMeasurer.class.st create mode 100644 src/Toplo/ToTripletLayoutHorizontalOrientation.class.st create mode 100644 src/Toplo/ToTripletLayoutMeasurer.class.st create mode 100644 src/Toplo/ToTripletLayoutNode.class.st create mode 100644 src/Toplo/ToTripletLayoutOrientation.class.st create mode 100644 src/Toplo/ToTripletLayoutVerticalMeasurer.class.st create mode 100644 src/Toplo/ToTripletLayoutVerticalOrientation.class.st diff --git a/src/Toplo-Examples/ToSandBox.class.st b/src/Toplo-Examples/ToSandBox.class.st index 56d2ee308..f7587c328 100644 --- a/src/Toplo-Examples/ToSandBox.class.st +++ b/src/Toplo-Examples/ToSandBox.class.st @@ -9437,12 +9437,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 a3a8eccc3..f2df73023 100644 --- a/src/Toplo/TToTripletElement.trait.st +++ b/src/Toplo/TToTripletElement.trait.st @@ -8,7 +8,7 @@ Trait { 'middleElement', 'endElement' ], - #category : #'Toplo-Core' + #category : #'Toplo-Core-Triplet' } { #category : #'t - triplet element - accessing' } @@ -215,6 +215,7 @@ TToTripletElement >> initializeAsTripletElement [ "the midddle container must be created because other parts are added according to its position" self createMiddleContainer. + self layout: self defaultLayout. self addEventHandler: self defaultTripletEventHandler ] diff --git a/src/Toplo/TToTripletElementConfiguration.trait.st b/src/Toplo/TToTripletElementConfiguration.trait.st index e69ed217e..cffc2d168 100644 --- a/src/Toplo/TToTripletElementConfiguration.trait.st +++ b/src/Toplo/TToTripletElementConfiguration.trait.st @@ -13,7 +13,7 @@ Trait { 'startInterspace', 'alignCenter' ], - #category : #'Toplo-Core' + #category : #'Toplo-Core-Triplet' } { #category : #'t - triplet element accessing' } @@ -52,12 +52,12 @@ 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 ] ]. + child constraints triplet horizontal alignCenter. + ] ]. alignCenter ifTrue: [ anElement layout alignCenter ] ifFalse: [ anElement layout alignCenterLeft ]. 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 24e45bc2a..62f5a07f5 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. ] 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..8af983212 --- /dev/null +++ b/src/Toplo/ToTripletLayout.class.st @@ -0,0 +1,279 @@ +Class { + #name : #ToTripletLayout, + #superclass : #BlLayout, + #traits : 'TBlAlignable + TBlOrientableLayout', + #classTraits : 'TBlAlignable classTrait + TBlOrientableLayout classTrait', + #instVars : [ + 'weightSum', + 'verticalAlignment', + 'horizontalAlignment', + 'orientation', + 'shouldUseLargestChild', + 'cellSpacing', + 'direction', + 'interspace' + ], + #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 : #accessing } +ToTripletLayout >> cellSpacing [ + + + ^ cellSpacing +] + +{ #category : #accessing } +ToTripletLayout >> cellSpacing: aNumber [ + cellSpacing := aNumber asFloat +] + +{ #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 : #initialization } +ToTripletLayout >> defaultWeightSum [ + "Default weight sum should be 0 to indicate that actual weight should be calculated + as arithmetic sum of childrens' weight" + + + ^ 0 +] + +{ #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. + + weightSum := self defaultWeightSum. + shouldUseLargestChild := false. + cellSpacing := 0.0. + interspace := 0.0. + verticalAlignment := self defaultVerticalAlignment. + horizontalAlignment := self defaultHorizontalAlignment. + + self orientation: self defaultOrientation +] + +{ #category : #accessing } +ToTripletLayout >> interspace [ + + ^ interspace +] + +{ #category : #accessing } +ToTripletLayout >> interspace: aNumber [ + + interspace := aNumber asFloat +] + +{ #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 : #testing } +ToTripletLayout >> shouldUseLargestChild [ + "When true, all children with a weight will be considered having the minimum size of the largest child. + If false, all children are measured normally." + + + ^ shouldUseLargestChild +] + +{ #category : #accessing } +ToTripletLayout >> verticalAlignment [ + ^ verticalAlignment +] + +{ #category : #accessing } +ToTripletLayout >> verticalAlignment: anAlignment [ + verticalAlignment := anAlignment +] + +{ #category : #accessing } +ToTripletLayout >> weightSum [ + + + ^ weightSum +] + +{ #category : #accessing } +ToTripletLayout >> weightSum: aNumber [ + weightSum := aNumber asFloat +] + +{ #category : #'api - configuration' } +ToTripletLayout >> withSpaceEqualization [ + "When set to true, all children with a weight will be considered having the minimum size of the largest child. + If false, all children are measured normally. + Disabled by default." + + shouldUseLargestChild := true +] + +{ #category : #'api - configuration' } +ToTripletLayout >> withoutSpaceEqualization [ + "When set to true, all children with a weight will be considered having the minimum size of the largest child. + If false, all children are measured normally. + Disabled by default." + + shouldUseLargestChild := false +] 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..ccf953406 --- /dev/null +++ b/src/Toplo/ToTripletLayoutHorizontalMeasurer.class.st @@ -0,0 +1,100 @@ +Class { + #name : #ToTripletLayoutHorizontalMeasurer, + #superclass : #ToTripletLayoutMeasurer, + #instVars : [ + 'maxAscent', + 'maxDescent' + ], + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #measurement } +ToTripletLayoutHorizontalMeasurer >> baselineMeasure [ + "I adjust my max breadth based on current max ascent and descent" + + maxAscent ifNotEmpty: [ + | anAscent aDescent | + + anAscent := maxAscent values max. + aDescent := maxDescent values max. + maxBreadth := maxBreadth max: (anAscent + aDescent) ] +] + +{ #category : #initialization } +ToTripletLayoutHorizontalMeasurer >> initialize [ + super initialize. + + maxAscent := Dictionary new. + maxDescent := Dictionary new +] + +{ #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) ]. + + "Baseline alignment requires to measure widgets to obtain the baseline offset (in particular for TextElements). + The following defeats the optimization mentioned above. + Allow the child to use as much space as it wants because we can shrink things later (and re-measure)." + self isBaselineAligned + ifTrue: [ aNode measure: BlExtentMeasurementSpec unspecified ] + ifFalse: [ skippedMeasure := true ] +] + +{ #category : #'private - measurement' } +ToTripletLayoutHorizontalMeasurer >> prepareForWeightedMeasure [ + maxAscent removeAll. + maxDescent removeAll. + + totalLength := 0.0. + + self flag: 'Why reset max breadth?'. + maxBreadth := 0.0. +] + +{ #category : #'private - measurement' } +ToTripletLayoutHorizontalMeasurer >> processBaseline: aNode breadth: aChildBreadth [ + "I process baseline of a given child node if it is supported. I update max ascent and max descent accordingly" + | childBaseline childAlignment | + + (self isBaselineAligned and: [ aNode element supportsBaseline ]) + ifFalse: [ ^ self ]. + + childBaseline := aNode element baselineOffset. + childAlignment := aNode verticalAlignment ifNull: [ layout verticalAlignment ]. + + maxAscent + at: childAlignment + ifPresent: [ :anAscent | maxAscent at: childAlignment put: (anAscent max: childBaseline) ] + ifAbsentPut: [ childBaseline ]. + maxDescent + at: childAlignment + ifPresent: [ :aDescent | maxDescent at: childAlignment put: (aDescent max: aChildBreadth - childBaseline) ] + ifAbsentPut: [ aChildBreadth - childBaseline ] +] + +{ #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) ] +] + +{ #category : #'private - measurement' } +ToTripletLayoutHorizontalMeasurer >> updateTotalLengthFromLargestChild: aNode [ + "I update the total length taking into account the length of the largest child" + + self flag: 'Why accounting for isBreadthExact is needed for the horizontal layout?'. + + totalLength := self isBreadthExact + ifTrue: [ totalLength + largestChildLength + aNode marginLength ] + ifFalse: [ totalLength max: (totalLength + largestChildLength + aNode marginLength) ] +] diff --git a/src/Toplo/ToTripletLayoutHorizontalOrientation.class.st b/src/Toplo/ToTripletLayoutHorizontalOrientation.class.st new file mode 100644 index 000000000..b474c9d8c --- /dev/null +++ b/src/Toplo/ToTripletLayoutHorizontalOrientation.class.st @@ -0,0 +1,185 @@ +Class { + #name : #ToTripletLayoutHorizontalOrientation, + #superclass : #ToTripletLayoutOrientation, + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> breadthMode [ + ^ self layout cache heightMode +] + +{ #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 turn | + "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. + + turn := 1. + self layout direction + with: theLayeredChildren + inject: left @ top + into: [ :origin :child | + | childBounds childMarginBounds childConstraints interspacing | + childConstraints := child constraints linear. + 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 ]. + + childBounds := childBounds translateBy: (self layout cellSpacing @ 0.0). + interspacing := turn = 1 + ifTrue: [ 0.0 ] + ifFalse: [ self layout interspace ]. + interspacing > 0.0 ifTrue: [ childBounds := childBounds translateBy: (turn - 1 * interspacing) @ 0.0 ]. + + "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" + turn := turn + 1. + (origin x + child measuredWidth + (child margin width max: 0.0) + self layout cellSpacing) @ origin y ] +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> lengthLinearProperties: aChildElement [ + ^ aChildElement constraints linear horizontal +] + +{ #category : #orientation } +ToTripletLayoutHorizontalOrientation >> lengthMode [ + ^ self layout cache widthMode +] + +{ #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..855de102b --- /dev/null +++ b/src/Toplo/ToTripletLayoutMeasurer.class.st @@ -0,0 +1,510 @@ +Class { + #name : #ToTripletLayoutMeasurer, + #superclass : #Object, + #instVars : [ + 'isBaselineAligned', + 'useLargestChild', + 'cellSpacing', + 'nodes', + 'parent', + 'breadthSpec', + 'lengthSpec', + 'orientation', + 'totalLength', + 'totalWeight', + 'skippedMeasure', + 'extentSpec', + 'usedExcessSpace', + 'largestChildLength', + 'matchBreadth', + 'maxBreadth', + 'allFillParent', + 'weightedMaxBreadth', + 'alternativeMaxBreadth', + 'measuredLength', + 'layout', + 'interSpacing' + ], + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #measurement } +ToTripletLayoutMeasurer >> baselineMeasure [ + self subclassResponsibility +] + +{ #category : #accessing } +ToTripletLayoutMeasurer >> breadthSpec [ + ^ breadthSpec +] + +{ #category : #measurement } +ToTripletLayoutMeasurer >> canMeasureExactUsingExcessSpaceForNode: aNode [ + + ^ self isLengthExact and: [ self shouldUseExcessSpaceForNode: aNode ] +] + +{ #category : #'accessing - properties' } +ToTripletLayoutMeasurer >> cellSpacing [ + ^ cellSpacing +] + +{ #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. + + useLargestChild := false. + isBaselineAligned := false. + cellSpacing := 0.0. + interSpacing := 0.0. + + totalLength := 0.0. + totalWeight := 0.0. + usedExcessSpace := 0.0. + skippedMeasure := false. + matchBreadth := false. + largestChildLength := 0.0. + measuredLength := 0.0. + alternativeMaxBreadth := 0.0. + maxBreadth := 0.0. + allFillParent := true. + weightedMaxBreadth := 0.0 +] + +{ #category : #initialization } +ToTripletLayoutMeasurer >> initializeWith: aParentElement layout: aLinearLayout extentSpec: anExtentSpec orientation: aToTripletLayoutOrientation [ + + parent := aParentElement. + orientation := aToTripletLayoutOrientation. + layout := aLinearLayout. + useLargestChild := layout shouldUseLargestChild. + cellSpacing := layout cellSpacing. + interSpacing := layout interspace. + + extentSpec := anExtentSpec. + breadthSpec := orientation breadthSpec: anExtentSpec. + lengthSpec := orientation lengthSpec: anExtentSpec. + + nodes := aParentElement children accountedByLayout + collect: [ :aChildElement | ToTripletLayoutNode new initializeWith: aChildElement orientation: orientation ] +] + +{ #category : #accessing } +ToTripletLayoutMeasurer >> interspace [ + + ^ interSpacing +] + +{ #category : #'accessing - properties' } +ToTripletLayoutMeasurer >> isBaselineAligned [ + ^ isBaselineAligned +] + +{ #category : #testing } +ToTripletLayoutMeasurer >> isBreadthExact [ + ^ self breadthSpec isExact +] + +{ #category : #testing } +ToTripletLayoutMeasurer >> isLengthExact [ + ^ self lengthSpec isExact +] + +{ #category : #measurement } +ToTripletLayoutMeasurer >> largestMeasure [ + "I re-measure my length for the case when we consider all weighted children to have the minimum size of the largest child" + + self useLargestChild + ifFalse: [ ^ self ]. + + (lengthSpec isAtMost or: [ lengthSpec isUnspecified ]) + ifFalse: [ ^ self ]. + + totalLength := 0. + + self nodes do: [ :aNode | self updateTotalLengthFromLargestChild: aNode ] +] + +{ #category : #'as yet unclassified' } +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 ]. + + self baselineMeasure. + self largestMeasure. + + 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 [ + + (allFillParent not and: [ breadthSpec isExact not ]) ifTrue: [ + maxBreadth := alternativeMaxBreadth ]. + + 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 size > 1 ifTrue: [ + totalLength := totalLength + ((self nodes size - 1) * self interspace) ]. + + self nodes do: [ :aNode | + | matchBreadthLocally childBreadth shouldUseExcessSpace | + + aNode hasSpaceBefore + ifTrue: [ totalLength := totalLength + self cellSpacing ]. + + 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. + + largestChildLength := largestChildLength max: childLength ]. + + self flag: 'Add baseline support here'. + + 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. + self processBaseline: aNode breadth: childBreadth. + + maxBreadth := maxBreadth max: childBreadth. + allFillParent := allFillParent and: [ aNode breadthResizer isMatchParent ]. + + aNode weight > 0 + ifTrue: [ + "Heights of weighted elements are bogus if we end up remeasuring, so keep them separate." + weightedMaxBreadth := (matchBreadthLocally + ifTrue: [ aNode marginBreadth ] + ifFalse: [ childBreadth ]) max: weightedMaxBreadth ] + ifFalse: [ + alternativeMaxBreadth := (matchBreadthLocally + ifTrue: [ aNode marginBreadth ] + ifFalse: [ childBreadth ]) max: alternativeMaxBreadth ] ]. + + self nodes ifNotEmpty: [ :theNodes | + theNodes last hasSpaceAfter + ifTrue: [ totalLength := totalLength + self cellSpacing ] ] +] + +{ #category : #'private - measurement' } +ToTripletLayoutMeasurer >> prepareForWeightedMeasure [ + self subclassResponsibility +] + +{ #category : #'private - measurement' } +ToTripletLayoutMeasurer >> processBaseline: aNode breadth: aChildBreadth [ + "I process baseline of a given child node if it is supported." + + self subclassResponsibility +] + +{ #category : #'accessing - properties' } +ToTripletLayoutMeasurer >> remainingWeightSum [ + + + ^ self weightSum > 0 + ifTrue: [ self weightSum ] + ifFalse: [ totalWeight ] +] + +{ #category : #'private - measurement' } +ToTripletLayoutMeasurer >> shouldUseExcessSpaceForNode: aNode [ + + ^ (lengthSpec isUnspecified not or: [ useLargestChild ]) 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 : #'private - measurement' } +ToTripletLayoutMeasurer >> updateTotalLengthFromLargestChild: aNode [ + "I update the total length taking into account the length of the largest child" + + self subclassResponsibility +] + +{ #category : #'accessing - properties' } +ToTripletLayoutMeasurer >> useLargestChild [ + ^ useLargestChild +] + +{ #category : #'accessing - properties' } +ToTripletLayoutMeasurer >> weightSum [ + ^ layout weightSum +] + +{ #category : #measurement } +ToTripletLayoutMeasurer >> weightedMeasure [ + "I measure weighted children if there is enough excess space" + | remainingExcess | + + "Check against our minimum length" + measuredLength := totalLength max: (orientation minimalLength: parent). + + "Reconcile our calculated size with the lengthSpec" + measuredLength := lengthSpec sizeFor: measuredLength. + + "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." + remainingExcess := measuredLength - totalLength + usedExcessSpace. + + (skippedMeasure or: [ remainingExcess > 0 and: [ totalWeight > 0 ] ]) + ifTrue: [ + | remainingWeightSum | + + remainingWeightSum := self remainingWeightSum. + + self prepareForWeightedMeasure. + + self nodes do: [ :aNode | + | childWeight childLength childBreadth matchBreadthLocally | + + childWeight := aNode weight. + childWeight > 0 + ifTrue: [ + | shareSpace childLengthSpec childBreadthSpec | + " Deal with precision " + shareSpace := (remainingWeightSum closeTo: 0) + ifTrue: [ 0.0 ] + ifFalse: [ (childWeight * remainingExcess / remainingWeightSum) asFloat ]. + + remainingExcess := remainingExcess - shareSpace. + remainingWeightSum := remainingWeightSum - childWeight. + + childLength := (self useLargestChild and: [ self isLengthExact not ]) + ifTrue: [ largestChildLength ] + ifFalse: [ aNode lengthResizer isMatchParent + "This child needs to be laid out from scratch using only its share of excess space." + ifTrue: [ shareSpace ] + "This child had some intrinsic width to which we need to add its share of excess space." + 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. + alternativeMaxBreadth := (matchBreadthLocally + ifTrue: [ aNode marginBreadth ] + ifFalse: [ childBreadth ]) max: alternativeMaxBreadth. + allFillParent := allFillParent and: [ aNode breadthResizer isMatchParent ]. + + self processBaseline: aNode breadth: childBreadth ]. + + totalLength := totalLength + (orientation paddingLength: parent). + self baselineMeasure ] + + ifFalse: [ + alternativeMaxBreadth := alternativeMaxBreadth max: weightedMaxBreadth. + + " We have no limit, so make all weighted views as wide as the largest child. + Children will have already been measured once." + (self useLargestChild and: [ self isLengthExact not ]) + ifTrue: [ + self nodes + select: [ :aNode | aNode weight > 0 ] + thenDo: [ :aNode | + aNode measure: (orientation + extentBreadth: (BlMeasurementSpec exact: aNode measuredBreadth) + lengthSpec: (BlMeasurementSpec exact: largestChildLength)) ] ] ] +] diff --git a/src/Toplo/ToTripletLayoutNode.class.st b/src/Toplo/ToTripletLayoutNode.class.st new file mode 100644 index 000000000..86d39cca3 --- /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 linear. +] + +{ #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..aa7ba332e --- /dev/null +++ b/src/Toplo/ToTripletLayoutOrientation.class.st @@ -0,0 +1,153 @@ +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 >> breadthMode [ + ^ self subclassResponsibility +] + +{ #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 >> lengthLinearProperties: anElement [ + ^ self subclassResponsibility +] + +{ #category : #orientation } +ToTripletLayoutOrientation >> lengthMode [ + ^ 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..4ada989f8 --- /dev/null +++ b/src/Toplo/ToTripletLayoutVerticalMeasurer.class.st @@ -0,0 +1,41 @@ +Class { + #name : #ToTripletLayoutVerticalMeasurer, + #superclass : #ToTripletLayoutMeasurer, + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #measurement } +ToTripletLayoutVerticalMeasurer >> baselineMeasure [ +] + +{ #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). + skippedMeasure := true +] + +{ #category : #'private - measurement' } +ToTripletLayoutVerticalMeasurer >> prepareForWeightedMeasure [ + totalLength := 0.0 +] + +{ #category : #measurement } +ToTripletLayoutVerticalMeasurer >> processBaseline: aNode breadth: aChildBreadth [ +] + +{ #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) +] + +{ #category : #'private - measurement' } +ToTripletLayoutVerticalMeasurer >> updateTotalLengthFromLargestChild: aNode [ + "I update the total length taking into account the length of the largest child" + + totalLength := totalLength max: (totalLength + largestChildLength + aNode marginLength) +] diff --git a/src/Toplo/ToTripletLayoutVerticalOrientation.class.st b/src/Toplo/ToTripletLayoutVerticalOrientation.class.st new file mode 100644 index 000000000..4eae2ae13 --- /dev/null +++ b/src/Toplo/ToTripletLayoutVerticalOrientation.class.st @@ -0,0 +1,186 @@ +Class { + #name : #ToTripletLayoutVerticalOrientation, + #superclass : #ToTripletLayoutOrientation, + #category : #'Toplo-Core-TripletLayout' +} + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> breadthMode [ + ^ self layout cache widthMode +] + +{ #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 turn 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. + + turn := 1. + self layout direction + with: anElement children accountedByLayout + inject: left @ top + into: [ :origin :child | + | childBounds childMarginBounds childConstraints interspacing | + childConstraints := child constraints linear. + 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 ]. + + childBounds := childBounds translateBy: (0.0 @ self layout cellSpacing). + + interspacing := turn = 1 + ifTrue: [ 0.0 ] + ifFalse: [ self layout interspace ]. + interspacing > 0.0 ifTrue: [ childBounds := childBounds translateBy: 0 @ (turn - 1 * interspacing) ]. + + "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" + turn := turn + 1. + origin x @(origin y + child measuredHeight + self layout cellSpacing + (child margin height max: 0.0)) ] +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> lengthLinearProperties: aChildElement [ + ^ aChildElement constraints linear vertical +] + +{ #category : #orientation } +ToTripletLayoutVerticalOrientation >> lengthMode [ + ^ self layout cache heightMode +] + +{ #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 +] From 60959879c52d83ce12be6ed2f78629ea4903f3b2 Mon Sep 17 00:00:00 2001 From: Alain Plantec Date: Sat, 19 Jul 2025 15:56:39 +0200 Subject: [PATCH 2/4] ToTripletLayout : remove weightSum shouldUseLargestChild cellSpacing interspace ToTripletLayoutMeasurer : remove isBaselineAligned useLargestChild cellSpacing ToTripletLayoutHorizontalMeasurer: remove maxAscent maxDescent --- src/Toplo/ToTripletLayout.class.st | 82 +----- ...ToTripletLayoutHorizontalMeasurer.class.st | 67 +---- ...ripletLayoutHorizontalOrientation.class.st | 14 +- src/Toplo/ToTripletLayoutMeasurer.class.st | 237 ++++-------------- .../ToTripletLayoutVerticalMeasurer.class.st | 18 +- ...oTripletLayoutVerticalOrientation.class.st | 17 +- 6 files changed, 52 insertions(+), 383 deletions(-) diff --git a/src/Toplo/ToTripletLayout.class.st b/src/Toplo/ToTripletLayout.class.st index 8af983212..31be5b811 100644 --- a/src/Toplo/ToTripletLayout.class.st +++ b/src/Toplo/ToTripletLayout.class.st @@ -4,14 +4,10 @@ Class { #traits : 'TBlAlignable + TBlOrientableLayout', #classTraits : 'TBlAlignable classTrait + TBlOrientableLayout classTrait', #instVars : [ - 'weightSum', 'verticalAlignment', 'horizontalAlignment', 'orientation', - 'shouldUseLargestChild', - 'cellSpacing', - 'direction', - 'interspace' + 'direction' ], #category : #'Toplo-Core-TripletLayout' } @@ -61,18 +57,6 @@ ToTripletLayout >> beVertical [ self orientation: ToTripletLayoutVerticalOrientation new ] -{ #category : #accessing } -ToTripletLayout >> cellSpacing [ - - - ^ cellSpacing -] - -{ #category : #accessing } -ToTripletLayout >> cellSpacing: aNumber [ - cellSpacing := aNumber asFloat -] - { #category : #initialization } ToTripletLayout >> defaultHorizontalAlignment [ "By default alignment should not influence on layout process, @@ -101,15 +85,6 @@ ToTripletLayout >> defaultVerticalAlignment [ ^ BlElementAlignment null vertical ] -{ #category : #initialization } -ToTripletLayout >> defaultWeightSum [ - "Default weight sum should be 0 to indicate that actual weight should be calculated - as arithmetic sum of childrens' weight" - - - ^ 0 -] - { #category : #'api - direction' } ToTripletLayout >> direction [ @@ -137,28 +112,12 @@ ToTripletLayout >> initialize [ super initialize. - weightSum := self defaultWeightSum. - shouldUseLargestChild := false. - cellSpacing := 0.0. - interspace := 0.0. verticalAlignment := self defaultVerticalAlignment. horizontalAlignment := self defaultHorizontalAlignment. self orientation: self defaultOrientation ] -{ #category : #accessing } -ToTripletLayout >> interspace [ - - ^ interspace -] - -{ #category : #accessing } -ToTripletLayout >> interspace: aNumber [ - - interspace := aNumber asFloat -] - { #category : #layout } ToTripletLayout >> layout: anElement in: aRectangle context: aBlElementBoundsUpdateContext [ self orientation layout: anElement in: aRectangle context: aBlElementBoundsUpdateContext. @@ -229,15 +188,6 @@ ToTripletLayout >> rightToLeft [ self direction: BlLayoutDirection rightToLeft ] -{ #category : #testing } -ToTripletLayout >> shouldUseLargestChild [ - "When true, all children with a weight will be considered having the minimum size of the largest child. - If false, all children are measured normally." - - - ^ shouldUseLargestChild -] - { #category : #accessing } ToTripletLayout >> verticalAlignment [ ^ verticalAlignment @@ -247,33 +197,3 @@ ToTripletLayout >> verticalAlignment [ ToTripletLayout >> verticalAlignment: anAlignment [ verticalAlignment := anAlignment ] - -{ #category : #accessing } -ToTripletLayout >> weightSum [ - - - ^ weightSum -] - -{ #category : #accessing } -ToTripletLayout >> weightSum: aNumber [ - weightSum := aNumber asFloat -] - -{ #category : #'api - configuration' } -ToTripletLayout >> withSpaceEqualization [ - "When set to true, all children with a weight will be considered having the minimum size of the largest child. - If false, all children are measured normally. - Disabled by default." - - shouldUseLargestChild := true -] - -{ #category : #'api - configuration' } -ToTripletLayout >> withoutSpaceEqualization [ - "When set to true, all children with a weight will be considered having the minimum size of the largest child. - If false, all children are measured normally. - Disabled by default." - - shouldUseLargestChild := false -] diff --git a/src/Toplo/ToTripletLayoutHorizontalMeasurer.class.st b/src/Toplo/ToTripletLayoutHorizontalMeasurer.class.st index ccf953406..4c7f6cf10 100644 --- a/src/Toplo/ToTripletLayoutHorizontalMeasurer.class.st +++ b/src/Toplo/ToTripletLayoutHorizontalMeasurer.class.st @@ -1,33 +1,9 @@ Class { #name : #ToTripletLayoutHorizontalMeasurer, #superclass : #ToTripletLayoutMeasurer, - #instVars : [ - 'maxAscent', - 'maxDescent' - ], #category : #'Toplo-Core-TripletLayout' } -{ #category : #measurement } -ToTripletLayoutHorizontalMeasurer >> baselineMeasure [ - "I adjust my max breadth based on current max ascent and descent" - - maxAscent ifNotEmpty: [ - | anAscent aDescent | - - anAscent := maxAscent values max. - aDescent := maxDescent values max. - maxBreadth := maxBreadth max: (anAscent + aDescent) ] -] - -{ #category : #initialization } -ToTripletLayoutHorizontalMeasurer >> initialize [ - super initialize. - - maxAscent := Dictionary new. - maxDescent := Dictionary new -] - { #category : #'private - measurement' } ToTripletLayoutHorizontalMeasurer >> measureExactUsingExcessSpace: aNode [ "Optimization: don't bother measuring children who are only laid out using excess space. @@ -35,20 +11,11 @@ ToTripletLayoutHorizontalMeasurer >> measureExactUsingExcessSpace: aNode [ totalLength := self isLengthExact ifTrue: [ totalLength + aNode marginLength ] - ifFalse: [ totalLength max: (totalLength + aNode marginLength) ]. - - "Baseline alignment requires to measure widgets to obtain the baseline offset (in particular for TextElements). - The following defeats the optimization mentioned above. - Allow the child to use as much space as it wants because we can shrink things later (and re-measure)." - self isBaselineAligned - ifTrue: [ aNode measure: BlExtentMeasurementSpec unspecified ] - ifFalse: [ skippedMeasure := true ] + ifFalse: [ totalLength max: (totalLength + aNode marginLength) ] ] { #category : #'private - measurement' } ToTripletLayoutHorizontalMeasurer >> prepareForWeightedMeasure [ - maxAscent removeAll. - maxDescent removeAll. totalLength := 0.0. @@ -56,27 +23,6 @@ ToTripletLayoutHorizontalMeasurer >> prepareForWeightedMeasure [ maxBreadth := 0.0. ] -{ #category : #'private - measurement' } -ToTripletLayoutHorizontalMeasurer >> processBaseline: aNode breadth: aChildBreadth [ - "I process baseline of a given child node if it is supported. I update max ascent and max descent accordingly" - | childBaseline childAlignment | - - (self isBaselineAligned and: [ aNode element supportsBaseline ]) - ifFalse: [ ^ self ]. - - childBaseline := aNode element baselineOffset. - childAlignment := aNode verticalAlignment ifNull: [ layout verticalAlignment ]. - - maxAscent - at: childAlignment - ifPresent: [ :anAscent | maxAscent at: childAlignment put: (anAscent max: childBaseline) ] - ifAbsentPut: [ childBaseline ]. - maxDescent - at: childAlignment - ifPresent: [ :aDescent | maxDescent at: childAlignment put: (aDescent max: aChildBreadth - childBaseline) ] - ifAbsentPut: [ aChildBreadth - childBaseline ] -] - { #category : #'private - measurement' } ToTripletLayoutHorizontalMeasurer >> updateTotalLengthAfterMeasuring: aNode [ "I update the total length after measuring a given node based on its measured length and margin" @@ -87,14 +33,3 @@ ToTripletLayoutHorizontalMeasurer >> updateTotalLengthAfterMeasuring: aNode [ ifTrue: [ totalLength + aNode measuredLength + aNode marginLength ] ifFalse: [ totalLength max: (totalLength + aNode measuredLength + aNode marginLength) ] ] - -{ #category : #'private - measurement' } -ToTripletLayoutHorizontalMeasurer >> updateTotalLengthFromLargestChild: aNode [ - "I update the total length taking into account the length of the largest child" - - self flag: 'Why accounting for isBreadthExact is needed for the horizontal layout?'. - - totalLength := self isBreadthExact - ifTrue: [ totalLength + largestChildLength + aNode marginLength ] - ifFalse: [ totalLength max: (totalLength + largestChildLength + aNode marginLength) ] -] diff --git a/src/Toplo/ToTripletLayoutHorizontalOrientation.class.st b/src/Toplo/ToTripletLayoutHorizontalOrientation.class.st index b474c9d8c..4e195699f 100644 --- a/src/Toplo/ToTripletLayoutHorizontalOrientation.class.st +++ b/src/Toplo/ToTripletLayoutHorizontalOrientation.class.st @@ -45,7 +45,7 @@ ToTripletLayoutHorizontalOrientation >> isHorizontal [ { #category : #layout } ToTripletLayoutHorizontalOrientation >> layout: anElement in: aRectangle context: aBlElementBoundsUpdateContext [ - |top right bottom left majorBounds elementInnerBounds theLayeredChildren turn | + |top right bottom left majorBounds elementInnerBounds theLayeredChildren | "Layout subnodes vertically in column one by one based on previousely measured extent" @@ -74,12 +74,11 @@ ToTripletLayoutHorizontalOrientation >> layout: anElement in: aRectangle context left := majorBounds left. top := majorBounds top. - turn := 1. self layout direction with: theLayeredChildren inject: left @ top into: [ :origin :child | - | childBounds childMarginBounds childConstraints interspacing | + | childBounds childMarginBounds childConstraints | childConstraints := child constraints linear. 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). @@ -96,19 +95,12 @@ ToTripletLayoutHorizontalOrientation >> layout: anElement in: aRectangle context translation := childConstraints vertical alignment translationOf: childMarginBounds in: allowedBounds. childBounds := childBounds translateBy: translation ]. - childBounds := childBounds translateBy: (self layout cellSpacing @ 0.0). - interspacing := turn = 1 - ifTrue: [ 0.0 ] - ifFalse: [ self layout interspace ]. - interspacing > 0.0 ifTrue: [ childBounds := childBounds translateBy: (turn - 1 * interspacing) @ 0.0 ]. - "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" - turn := turn + 1. - (origin x + child measuredWidth + (child margin width max: 0.0) + self layout cellSpacing) @ origin y ] + (origin x + child measuredWidth + (child margin width max: 0.0)) @ origin y ] ] { #category : #orientation } diff --git a/src/Toplo/ToTripletLayoutMeasurer.class.st b/src/Toplo/ToTripletLayoutMeasurer.class.st index 855de102b..60dc5aee7 100644 --- a/src/Toplo/ToTripletLayoutMeasurer.class.st +++ b/src/Toplo/ToTripletLayoutMeasurer.class.st @@ -2,9 +2,6 @@ Class { #name : #ToTripletLayoutMeasurer, #superclass : #Object, #instVars : [ - 'isBaselineAligned', - 'useLargestChild', - 'cellSpacing', 'nodes', 'parent', 'breadthSpec', @@ -12,27 +9,16 @@ Class { 'orientation', 'totalLength', 'totalWeight', - 'skippedMeasure', 'extentSpec', 'usedExcessSpace', - 'largestChildLength', 'matchBreadth', 'maxBreadth', - 'allFillParent', - 'weightedMaxBreadth', - 'alternativeMaxBreadth', 'measuredLength', - 'layout', - 'interSpacing' + 'layout' ], #category : #'Toplo-Core-TripletLayout' } -{ #category : #measurement } -ToTripletLayoutMeasurer >> baselineMeasure [ - self subclassResponsibility -] - { #category : #accessing } ToTripletLayoutMeasurer >> breadthSpec [ ^ breadthSpec @@ -44,11 +30,6 @@ ToTripletLayoutMeasurer >> canMeasureExactUsingExcessSpaceForNode: aNode [ ^ self isLengthExact and: [ self shouldUseExcessSpaceForNode: aNode ] ] -{ #category : #'accessing - properties' } -ToTripletLayoutMeasurer >> cellSpacing [ - ^ cellSpacing -] - { #category : #'private - measurement' } ToTripletLayoutMeasurer >> childBreadthSpecForNode: aNode [ @@ -63,22 +44,12 @@ ToTripletLayoutMeasurer >> childBreadthSpecForNode: aNode [ ToTripletLayoutMeasurer >> initialize [ super initialize. - useLargestChild := false. - isBaselineAligned := false. - cellSpacing := 0.0. - interSpacing := 0.0. - totalLength := 0.0. totalWeight := 0.0. usedExcessSpace := 0.0. - skippedMeasure := false. matchBreadth := false. - largestChildLength := 0.0. measuredLength := 0.0. - alternativeMaxBreadth := 0.0. - maxBreadth := 0.0. - allFillParent := true. - weightedMaxBreadth := 0.0 + maxBreadth := 0.0 ] { #category : #initialization } @@ -87,9 +58,6 @@ ToTripletLayoutMeasurer >> initializeWith: aParentElement layout: aLinearLayout parent := aParentElement. orientation := aToTripletLayoutOrientation. layout := aLinearLayout. - useLargestChild := layout shouldUseLargestChild. - cellSpacing := layout cellSpacing. - interSpacing := layout interspace. extentSpec := anExtentSpec. breadthSpec := orientation breadthSpec: anExtentSpec. @@ -99,17 +67,6 @@ ToTripletLayoutMeasurer >> initializeWith: aParentElement layout: aLinearLayout collect: [ :aChildElement | ToTripletLayoutNode new initializeWith: aChildElement orientation: orientation ] ] -{ #category : #accessing } -ToTripletLayoutMeasurer >> interspace [ - - ^ interSpacing -] - -{ #category : #'accessing - properties' } -ToTripletLayoutMeasurer >> isBaselineAligned [ - ^ isBaselineAligned -] - { #category : #testing } ToTripletLayoutMeasurer >> isBreadthExact [ ^ self breadthSpec isExact @@ -120,22 +77,7 @@ ToTripletLayoutMeasurer >> isLengthExact [ ^ self lengthSpec isExact ] -{ #category : #measurement } -ToTripletLayoutMeasurer >> largestMeasure [ - "I re-measure my length for the case when we consider all weighted children to have the minimum size of the largest child" - - self useLargestChild - ifFalse: [ ^ self ]. - - (lengthSpec isAtMost or: [ lengthSpec isUnspecified ]) - ifFalse: [ ^ self ]. - - totalLength := 0. - - self nodes do: [ :aNode | self updateTotalLengthFromLargestChild: aNode ] -] - -{ #category : #'as yet unclassified' } +{ #category : #accessing } ToTripletLayoutMeasurer >> layout [ ^ layout @@ -211,8 +153,7 @@ ToTripletLayoutMeasurer >> measureOn: anElement [ anElement ignoreRequestLayoutDuring: [ self preMeasure ]. - self baselineMeasure. - self largestMeasure. +" self baselineMeasure." totalLength := totalLength + (orientation paddingLength: parent). @@ -245,9 +186,6 @@ ToTripletLayoutMeasurer >> parent [ { #category : #measurement } ToTripletLayoutMeasurer >> postMeasure [ - (allFillParent not and: [ breadthSpec isExact not ]) ifTrue: [ - maxBreadth := alternativeMaxBreadth ]. - maxBreadth := maxBreadth + (orientation paddingBreadth: parent). "Check against our minimum height" @@ -263,15 +201,10 @@ ToTripletLayoutMeasurer >> postMeasure [ ToTripletLayoutMeasurer >> preMeasure [ "See how wide/tall everyone is. Also remember max breadth." - self nodes size > 1 ifTrue: [ - totalLength := totalLength + ((self nodes size - 1) * self interspace) ]. self nodes do: [ :aNode | | matchBreadthLocally childBreadth shouldUseExcessSpace | - - aNode hasSpaceBefore - ifTrue: [ totalLength := totalLength + self cellSpacing ]. - + totalWeight := totalWeight + aNode weight. "we must store the result because it may temporary change during measurement" @@ -309,11 +242,7 @@ ToTripletLayoutMeasurer >> preMeasure [ aNode lengthResizer: BlLayoutResizer matchParent. usedExcessSpace := usedExcessSpace + childLength ]. - self updateTotalLengthAfterMeasuring: aNode. - - largestChildLength := largestChildLength max: childLength ]. - - self flag: 'Add baseline support here'. + self updateTotalLengthAfterMeasuring: aNode ]. matchBreadthLocally := false. (self isBreadthExact not and: [ aNode breadthResizer isMatchParent ]) @@ -324,25 +253,8 @@ ToTripletLayoutMeasurer >> preMeasure [ matchBreadthLocally := true ]. childBreadth := aNode measuredBreadth + aNode marginBreadth. - self processBaseline: aNode breadth: childBreadth. - maxBreadth := maxBreadth max: childBreadth. - allFillParent := allFillParent and: [ aNode breadthResizer isMatchParent ]. - - aNode weight > 0 - ifTrue: [ - "Heights of weighted elements are bogus if we end up remeasuring, so keep them separate." - weightedMaxBreadth := (matchBreadthLocally - ifTrue: [ aNode marginBreadth ] - ifFalse: [ childBreadth ]) max: weightedMaxBreadth ] - ifFalse: [ - alternativeMaxBreadth := (matchBreadthLocally - ifTrue: [ aNode marginBreadth ] - ifFalse: [ childBreadth ]) max: alternativeMaxBreadth ] ]. - - self nodes ifNotEmpty: [ :theNodes | - theNodes last hasSpaceAfter - ifTrue: [ totalLength := totalLength + self cellSpacing ] ] + maxBreadth := maxBreadth max: childBreadth ] ] { #category : #'private - measurement' } @@ -350,26 +262,16 @@ ToTripletLayoutMeasurer >> prepareForWeightedMeasure [ self subclassResponsibility ] -{ #category : #'private - measurement' } -ToTripletLayoutMeasurer >> processBaseline: aNode breadth: aChildBreadth [ - "I process baseline of a given child node if it is supported." - - self subclassResponsibility -] - { #category : #'accessing - properties' } ToTripletLayoutMeasurer >> remainingWeightSum [ - - ^ self weightSum > 0 - ifTrue: [ self weightSum ] - ifFalse: [ totalWeight ] + ^ totalWeight ] { #category : #'private - measurement' } ToTripletLayoutMeasurer >> shouldUseExcessSpaceForNode: aNode [ - ^ (lengthSpec isUnspecified not or: [ useLargestChild ]) and: [ + ^ (lengthSpec isUnspecified not) and: [ aNode shouldUseExcessSpace ] ] @@ -412,99 +314,44 @@ ToTripletLayoutMeasurer >> updateTotalLengthAfterMeasuring: aNode [ self subclassResponsibility ] -{ #category : #'private - measurement' } -ToTripletLayoutMeasurer >> updateTotalLengthFromLargestChild: aNode [ - "I update the total length taking into account the length of the largest child" - - self subclassResponsibility -] - -{ #category : #'accessing - properties' } -ToTripletLayoutMeasurer >> useLargestChild [ - ^ useLargestChild -] - -{ #category : #'accessing - properties' } -ToTripletLayoutMeasurer >> weightSum [ - ^ layout weightSum -] - { #category : #measurement } ToTripletLayoutMeasurer >> weightedMeasure [ - "I measure weighted children if there is enough excess space" - | remainingExcess | - "Check against our minimum length" - measuredLength := totalLength max: (orientation minimalLength: parent). - - "Reconcile our calculated size with the lengthSpec" + | remainingExcess remainingWeightSum | + measuredLength := totalLength max: + (orientation minimalLength: parent). measuredLength := lengthSpec sizeFor: measuredLength. - - "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." remainingExcess := measuredLength - totalLength + usedExcessSpace. - (skippedMeasure or: [ remainingExcess > 0 and: [ totalWeight > 0 ] ]) - ifTrue: [ - | remainingWeightSum | - - remainingWeightSum := self remainingWeightSum. - - self prepareForWeightedMeasure. - - self nodes do: [ :aNode | - | childWeight childLength childBreadth matchBreadthLocally | - - childWeight := aNode weight. - childWeight > 0 - ifTrue: [ - | shareSpace childLengthSpec childBreadthSpec | - " Deal with precision " - shareSpace := (remainingWeightSum closeTo: 0) - ifTrue: [ 0.0 ] - ifFalse: [ (childWeight * remainingExcess / remainingWeightSum) asFloat ]. - - remainingExcess := remainingExcess - shareSpace. - remainingWeightSum := remainingWeightSum - childWeight. - - childLength := (self useLargestChild and: [ self isLengthExact not ]) - ifTrue: [ largestChildLength ] - ifFalse: [ aNode lengthResizer isMatchParent - "This child needs to be laid out from scratch using only its share of excess space." - ifTrue: [ shareSpace ] - "This child had some intrinsic width to which we need to add its share of excess space." - 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. - alternativeMaxBreadth := (matchBreadthLocally - ifTrue: [ aNode marginBreadth ] - ifFalse: [ childBreadth ]) max: alternativeMaxBreadth. - allFillParent := allFillParent and: [ aNode breadthResizer isMatchParent ]. - - self processBaseline: aNode breadth: childBreadth ]. - - totalLength := totalLength + (orientation paddingLength: parent). - self baselineMeasure ] + (remainingExcess > 0 and: [ totalWeight > 0 ]) ifFalse: [ ^ self ]. + remainingWeightSum := self remainingWeightSum. - ifFalse: [ - alternativeMaxBreadth := alternativeMaxBreadth max: weightedMaxBreadth. - - " We have no limit, so make all weighted views as wide as the largest child. - Children will have already been measured once." - (self useLargestChild and: [ self isLengthExact not ]) - ifTrue: [ - self nodes - select: [ :aNode | aNode weight > 0 ] - thenDo: [ :aNode | - aNode measure: (orientation - extentBreadth: (BlMeasurementSpec exact: aNode measuredBreadth) - lengthSpec: (BlMeasurementSpec exact: largestChildLength)) ] ] ] + 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/ToTripletLayoutVerticalMeasurer.class.st b/src/Toplo/ToTripletLayoutVerticalMeasurer.class.st index 4ada989f8..6e480ce27 100644 --- a/src/Toplo/ToTripletLayoutVerticalMeasurer.class.st +++ b/src/Toplo/ToTripletLayoutVerticalMeasurer.class.st @@ -4,17 +4,12 @@ Class { #category : #'Toplo-Core-TripletLayout' } -{ #category : #measurement } -ToTripletLayoutVerticalMeasurer >> baselineMeasure [ -] - { #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). - skippedMeasure := true + totalLength := totalLength max: (totalLength + aNode marginLength) ] { #category : #'private - measurement' } @@ -22,20 +17,9 @@ ToTripletLayoutVerticalMeasurer >> prepareForWeightedMeasure [ totalLength := 0.0 ] -{ #category : #measurement } -ToTripletLayoutVerticalMeasurer >> processBaseline: aNode breadth: aChildBreadth [ -] - { #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) ] - -{ #category : #'private - measurement' } -ToTripletLayoutVerticalMeasurer >> updateTotalLengthFromLargestChild: aNode [ - "I update the total length taking into account the length of the largest child" - - totalLength := totalLength max: (totalLength + largestChildLength + aNode marginLength) -] diff --git a/src/Toplo/ToTripletLayoutVerticalOrientation.class.st b/src/Toplo/ToTripletLayoutVerticalOrientation.class.st index 4eae2ae13..b31a1cd29 100644 --- a/src/Toplo/ToTripletLayoutVerticalOrientation.class.st +++ b/src/Toplo/ToTripletLayoutVerticalOrientation.class.st @@ -45,7 +45,7 @@ ToTripletLayoutVerticalOrientation >> isVertical [ { #category : #layout } ToTripletLayoutVerticalOrientation >> layout: anElement in: aRectangle context: aBlElementBoundsUpdateContext [ - |top right bottom left majorBounds elementInnerBounds turn theLayeredChildren | + |top right bottom left majorBounds elementInnerBounds theLayeredChildren | "Layout subnodes vertically in column one by one based on previously measured extent" @@ -74,12 +74,11 @@ ToTripletLayoutVerticalOrientation >> layout: anElement in: aRectangle context: left := majorBounds left. top := majorBounds top. - turn := 1. self layout direction with: anElement children accountedByLayout inject: left @ top into: [ :origin :child | - | childBounds childMarginBounds childConstraints interspacing | + | childBounds childMarginBounds childConstraints | childConstraints := child constraints linear. 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). @@ -95,21 +94,13 @@ ToTripletLayoutVerticalOrientation >> layout: anElement in: aRectangle context: allowedBounds := childMarginBounds origin extent: elementInnerBounds width @ child measuredHeight. translation := (childConstraints horizontal alignment directed: self layout direction) translationOf: childMarginBounds in: allowedBounds. childBounds := childBounds translateBy: translation ]. - - childBounds := childBounds translateBy: (0.0 @ self layout cellSpacing). - - interspacing := turn = 1 - ifTrue: [ 0.0 ] - ifFalse: [ self layout interspace ]. - interspacing > 0.0 ifTrue: [ childBounds := childBounds translateBy: 0 @ (turn - 1 * interspacing) ]. - + "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" - turn := turn + 1. - origin x @(origin y + child measuredHeight + self layout cellSpacing + (child margin height max: 0.0)) ] + origin x @(origin y + child measuredHeight + (child margin height max: 0.0)) ] ] { #category : #orientation } From 3bf8c931a57fc9417509693bdfa7801b98dbd82e Mon Sep 17 00:00:00 2001 From: Alain Plantec Date: Sun, 20 Jul 2025 20:25:12 +0200 Subject: [PATCH 3/4] continue TripletLayout cleaning --- src/Toplo/TToTripletElement.trait.st | 12 +- src/Toplo/ToTripletElement.class.st | 12 -- src/Toplo/ToTripletLayout.class.st | 1 + ...ToTripletLayoutHorizontalMeasurer.class.st | 9 -- ...ripletLayoutHorizontalOrientation.class.st | 105 +++++++++--------- src/Toplo/ToTripletLayoutMeasurer.class.st | 13 ++- src/Toplo/ToTripletLayoutNode.class.st | 2 +- src/Toplo/ToTripletLayoutOrientation.class.st | 15 --- .../ToTripletLayoutVerticalMeasurer.class.st | 5 - ...oTripletLayoutVerticalOrientation.class.st | 17 +-- 10 files changed, 69 insertions(+), 122 deletions(-) diff --git a/src/Toplo/TToTripletElement.trait.st b/src/Toplo/TToTripletElement.trait.st index f2df73023..8815a82e9 100644 --- a/src/Toplo/TToTripletElement.trait.st +++ b/src/Toplo/TToTripletElement.trait.st @@ -283,10 +283,8 @@ TToTripletElement >> newFrameContainer [ 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: BlFrameLayout new; yourself ] @@ -298,10 +296,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 ] diff --git a/src/Toplo/ToTripletElement.class.st b/src/Toplo/ToTripletElement.class.st index 62f5a07f5..104a848bb 100644 --- a/src/Toplo/ToTripletElement.class.st +++ b/src/Toplo/ToTripletElement.class.st @@ -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 >> newWidgetConfiguration [ diff --git a/src/Toplo/ToTripletLayout.class.st b/src/Toplo/ToTripletLayout.class.st index 31be5b811..700d11aed 100644 --- a/src/Toplo/ToTripletLayout.class.st +++ b/src/Toplo/ToTripletLayout.class.st @@ -120,6 +120,7 @@ ToTripletLayout >> initialize [ { #category : #layout } ToTripletLayout >> layout: anElement in: aRectangle context: aBlElementBoundsUpdateContext [ + self orientation layout: anElement in: aRectangle context: aBlElementBoundsUpdateContext. self layoutIgnored: anElement context: aBlElementBoundsUpdateContext ] diff --git a/src/Toplo/ToTripletLayoutHorizontalMeasurer.class.st b/src/Toplo/ToTripletLayoutHorizontalMeasurer.class.st index 4c7f6cf10..d7fed2ca2 100644 --- a/src/Toplo/ToTripletLayoutHorizontalMeasurer.class.st +++ b/src/Toplo/ToTripletLayoutHorizontalMeasurer.class.st @@ -14,15 +14,6 @@ ToTripletLayoutHorizontalMeasurer >> measureExactUsingExcessSpace: aNode [ ifFalse: [ totalLength max: (totalLength + aNode marginLength) ] ] -{ #category : #'private - measurement' } -ToTripletLayoutHorizontalMeasurer >> prepareForWeightedMeasure [ - - totalLength := 0.0. - - self flag: 'Why reset max breadth?'. - maxBreadth := 0.0. -] - { #category : #'private - measurement' } ToTripletLayoutHorizontalMeasurer >> updateTotalLengthAfterMeasuring: aNode [ "I update the total length after measuring a given node based on its measured length and margin" diff --git a/src/Toplo/ToTripletLayoutHorizontalOrientation.class.st b/src/Toplo/ToTripletLayoutHorizontalOrientation.class.st index 4e195699f..c319fc38d 100644 --- a/src/Toplo/ToTripletLayoutHorizontalOrientation.class.st +++ b/src/Toplo/ToTripletLayoutHorizontalOrientation.class.st @@ -4,11 +4,6 @@ Class { #category : #'Toplo-Core-TripletLayout' } -{ #category : #orientation } -ToTripletLayoutHorizontalOrientation >> breadthMode [ - ^ self layout cache heightMode -] - { #category : #orientation } ToTripletLayoutHorizontalOrientation >> breadthProperties: anElement [ ^ anElement constraints vertical @@ -45,72 +40,80 @@ ToTripletLayoutHorizontalOrientation >> isHorizontal [ { #category : #layout } ToTripletLayoutHorizontalOrientation >> layout: anElement in: aRectangle context: aBlElementBoundsUpdateContext [ - |top right bottom left majorBounds elementInnerBounds theLayeredChildren | + + | 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. + 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. + 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 ]. + | 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 linear. - 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. + | 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 >> lengthLinearProperties: aChildElement [ - ^ aChildElement constraints linear horizontal -] - -{ #category : #orientation } -ToTripletLayoutHorizontalOrientation >> lengthMode [ - ^ self layout cache widthMode + 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 } diff --git a/src/Toplo/ToTripletLayoutMeasurer.class.st b/src/Toplo/ToTripletLayoutMeasurer.class.st index 60dc5aee7..370fe7dc4 100644 --- a/src/Toplo/ToTripletLayoutMeasurer.class.st +++ b/src/Toplo/ToTripletLayoutMeasurer.class.st @@ -63,8 +63,11 @@ ToTripletLayoutMeasurer >> initializeWith: aParentElement layout: aLinearLayout breadthSpec := orientation breadthSpec: anExtentSpec. lengthSpec := orientation lengthSpec: anExtentSpec. - nodes := aParentElement children accountedByLayout - collect: [ :aChildElement | ToTripletLayoutNode new initializeWith: aChildElement orientation: orientation ] + nodes := aParentElement children accountedByLayout collect: [ + :aChildElement | + ToTripletLayoutNode new + initializeWith: aChildElement + orientation: orientation ] ] { #category : #testing } @@ -153,8 +156,6 @@ ToTripletLayoutMeasurer >> measureOn: anElement [ anElement ignoreRequestLayoutDuring: [ self preMeasure ]. -" self baselineMeasure." - totalLength := totalLength + (orientation paddingLength: parent). "Check against our minimum length" @@ -259,7 +260,8 @@ ToTripletLayoutMeasurer >> preMeasure [ { #category : #'private - measurement' } ToTripletLayoutMeasurer >> prepareForWeightedMeasure [ - self subclassResponsibility + + totalLength := 0.0. ] { #category : #'accessing - properties' } @@ -327,6 +329,7 @@ ToTripletLayoutMeasurer >> weightedMeasure [ remainingWeightSum := self remainingWeightSum. self prepareForWeightedMeasure. + self nodes do: [ :aNode | | childWeight childLength childBreadth matchBreadthLocally | childWeight := aNode weight. diff --git a/src/Toplo/ToTripletLayoutNode.class.st b/src/Toplo/ToTripletLayoutNode.class.st index 86d39cca3..20714ddb0 100644 --- a/src/Toplo/ToTripletLayoutNode.class.st +++ b/src/Toplo/ToTripletLayoutNode.class.st @@ -51,7 +51,7 @@ ToTripletLayoutNode >> hasSpaceBefore [ ToTripletLayoutNode >> initializeWith: anElement orientation: aToTripletLayoutOrientation [ element := anElement. orientation := aToTripletLayoutOrientation. - linearConstraints := anElement constraints linear. + linearConstraints := anElement constraints triplet ] { #category : #'accessing - constraints' } diff --git a/src/Toplo/ToTripletLayoutOrientation.class.st b/src/Toplo/ToTripletLayoutOrientation.class.st index aa7ba332e..d9e2d4fb2 100644 --- a/src/Toplo/ToTripletLayoutOrientation.class.st +++ b/src/Toplo/ToTripletLayoutOrientation.class.st @@ -26,11 +26,6 @@ ToTripletLayoutOrientation >> = anObject [ ^ self class = anObject class ] -{ #category : #orientation } -ToTripletLayoutOrientation >> breadthMode [ - ^ self subclassResponsibility -] - { #category : #orientation } ToTripletLayoutOrientation >> breadthProperties: anElement [ ^ self subclassResponsibility @@ -87,16 +82,6 @@ ToTripletLayoutOrientation >> layout: anElement in: aRectangle context: aBlEleme ^ self subclassResponsibility ] -{ #category : #orientation } -ToTripletLayoutOrientation >> lengthLinearProperties: anElement [ - ^ self subclassResponsibility -] - -{ #category : #orientation } -ToTripletLayoutOrientation >> lengthMode [ - ^ self subclassResponsibility -] - { #category : #orientation } ToTripletLayoutOrientation >> lengthProperties: anElement [ ^ self subclassResponsibility diff --git a/src/Toplo/ToTripletLayoutVerticalMeasurer.class.st b/src/Toplo/ToTripletLayoutVerticalMeasurer.class.st index 6e480ce27..dd56b7c5a 100644 --- a/src/Toplo/ToTripletLayoutVerticalMeasurer.class.st +++ b/src/Toplo/ToTripletLayoutVerticalMeasurer.class.st @@ -12,11 +12,6 @@ ToTripletLayoutVerticalMeasurer >> measureExactUsingExcessSpace: aNode [ totalLength := totalLength max: (totalLength + aNode marginLength) ] -{ #category : #'private - measurement' } -ToTripletLayoutVerticalMeasurer >> prepareForWeightedMeasure [ - totalLength := 0.0 -] - { #category : #'private - measurement' } ToTripletLayoutVerticalMeasurer >> updateTotalLengthAfterMeasuring: aNode [ "I update the total length after measuring a given node based on its measured length and margin" diff --git a/src/Toplo/ToTripletLayoutVerticalOrientation.class.st b/src/Toplo/ToTripletLayoutVerticalOrientation.class.st index b31a1cd29..4c8a27325 100644 --- a/src/Toplo/ToTripletLayoutVerticalOrientation.class.st +++ b/src/Toplo/ToTripletLayoutVerticalOrientation.class.st @@ -4,11 +4,6 @@ Class { #category : #'Toplo-Core-TripletLayout' } -{ #category : #orientation } -ToTripletLayoutVerticalOrientation >> breadthMode [ - ^ self layout cache widthMode -] - { #category : #orientation } ToTripletLayoutVerticalOrientation >> breadthProperties: anElement [ ^ anElement constraints horizontal @@ -79,7 +74,7 @@ ToTripletLayoutVerticalOrientation >> layout: anElement in: aRectangle context: inject: left @ top into: [ :origin :child | | childBounds childMarginBounds childConstraints | - childConstraints := child constraints linear. + 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). @@ -103,16 +98,6 @@ ToTripletLayoutVerticalOrientation >> layout: anElement in: aRectangle context: origin x @(origin y + child measuredHeight + (child margin height max: 0.0)) ] ] -{ #category : #orientation } -ToTripletLayoutVerticalOrientation >> lengthLinearProperties: aChildElement [ - ^ aChildElement constraints linear vertical -] - -{ #category : #orientation } -ToTripletLayoutVerticalOrientation >> lengthMode [ - ^ self layout cache heightMode -] - { #category : #orientation } ToTripletLayoutVerticalOrientation >> lengthProperties: aChildElement [ ^ aChildElement constraints vertical From bc96307c004dfba77450cd3fa847cf24dbc47ef6 Mon Sep 17 00:00:00 2001 From: Alain Plantec Date: Mon, 21 Jul 2025 12:40:40 +0200 Subject: [PATCH 4/4] TToTripletElement: remove alignCenter inst var --- src/Toplo/TToTripletElement.trait.st | 34 +++------ .../TToTripletElementConfiguration.trait.st | 72 +++++++------------ src/Toplo/ToTripletLayoutMeasurer.class.st | 2 +- 3 files changed, 33 insertions(+), 75 deletions(-) diff --git a/src/Toplo/TToTripletElement.trait.st b/src/Toplo/TToTripletElement.trait.st index 8815a82e9..8f2d2f7f4 100644 --- a/src/Toplo/TToTripletElement.trait.st +++ b/src/Toplo/TToTripletElement.trait.st @@ -11,18 +11,12 @@ Trait { #category : #'Toplo-Core-Triplet' } -{ #category : #'t - triplet element - accessing' } -TToTripletElement >> alignCenter [ - - ^ self widgetConfiguration alignCenter -] - { #category : #'t - triplet element - accessing' } TToTripletElement >> alignCenter: aBoolean [ - self alignCenter = aBoolean ifTrue: [ ^ self ]. - self widgetConfiguration alignCenter: aBoolean. - self requestNewConfiguration + aBoolean + ifTrue: [ self layout alignCenter ] + ifFalse: [ self layout alignCenterLeft ] ] { #category : #'t - triplet element - instance creation' } @@ -55,7 +49,7 @@ TToTripletElement >> checkStartContainer [ { #category : #'t - triplet element - instance creation' } TToTripletElement >> createEndContainer [ - endContainer := self newFrameContainer + endContainer := self newLinearContainer id: #'end-container'; yourself. self addChild: endContainer @@ -73,7 +67,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 @@ -216,6 +210,7 @@ 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 ] @@ -272,20 +267,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 triplet vertical alignCenter. - c triplet horizontal alignCenter ]; - layout: BlFrameLayout new; + layout: BlBasicLayout new; yourself ] @@ -305,7 +287,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 cffc2d168..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-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 triplet vertical alignCenter. - ] ] + child constraints triplet vertical alignCenter ] ] ifFalse: [ allChildren do: [ :child | - child constraints triplet 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/ToTripletLayoutMeasurer.class.st b/src/Toplo/ToTripletLayoutMeasurer.class.st index 370fe7dc4..f154fdad9 100644 --- a/src/Toplo/ToTripletLayoutMeasurer.class.st +++ b/src/Toplo/ToTripletLayoutMeasurer.class.st @@ -205,7 +205,7 @@ ToTripletLayoutMeasurer >> preMeasure [ self nodes do: [ :aNode | | matchBreadthLocally childBreadth shouldUseExcessSpace | - + totalWeight := totalWeight + aNode weight. "we must store the result because it may temporary change during measurement"