[Cuis-dev] BoxedMorph padding

ken.dickey at whidbey.com ken.dickey at whidbey.com
Wed Sep 4 14:02:42 PDT 2024


Greetings,

Attached is a fileIn for adding padding to BoxedMorphs.

Padding is really just a property for now -- simulated iVar but no 
change to BoxedMorph instance shape.

You can use a morph menu to get an inspector to make visible the 
borderWidth and add padding (an integer or point).

Current code should run unchanged.

Updated drawing with padding in:
   LayoutMorph
   ImageMorph
   InnerTextMorph
   LabelMorph
   TextParagraphMorph

Note that #minimumExtent: now includes both borderWidth and padding.

The question is: is it worthwhile to add #padding to BoxedMorph, or 
should we just add to LayoutMorph?

Anyway, please play around with padding, let me know of breakage, and 
are there additional Morphs where drawOn: updates would be useful?

Other features?

Thanks for feedback,
-KenD
-------------- next part --------------
'From Cuis7.1 [latest update: #6671] on 4 September 2024 at 12:14:49 pm'!

!BoxedMorph methodsFor: 'accessing' stamp: 'KenD 9/3/2024 15:04:00'!
padding
	"Answer a point"
	self flag: #FakeIvar.
	^self valueOfProperty: #padding ifAbsentPut: [ 0 @ 0 ]! !

!BoxedMorph methodsFor: 'accessing' stamp: 'KenD 9/4/2024 11:52:36'!
padding: numberOrPoint
	"Save a point"
	self flag: #FakeIvar.
	self setProperty: #padding
		toValue: ((numberOrPoint class = Point) 
			ifTrue: [numberOrPoint] 
			ifFalse: [numberOrPoint @ numberOrPoint]).
	self redrawNeeded! !

!BoxedMorph methodsFor: 'geometry interior' stamp: 'KenD 9/3/2024 15:24:23'!
interiorExtent

	"Answer extent inset from borderWidth and Padding"
	^self morphExtent - (2 * self borderWidth) - (2 * self padding)! !

!BoxedMorph methodsFor: 'geometry interior' stamp: 'KenD 9/4/2024 12:14:27'!
interiorOrigin

	"Answer point inset from borderWidth and Padding"
	| x y |
	x := self borderWidth + self padding x.
	y := (self borderWidth + self padding y) negated.
	^x @ (y negated)! !


!LayoutMorph methodsFor: 'accessing' stamp: 'KenD 9/4/2024 11:50:46'!
padding: numberOrPoint

	super padding: numberOrPoint.
	self layoutSubmorphs ! !


!LayoutMorph methodsFor: 'accessing' stamp: 'KenD 9/3/2024 15:45:58'!
useEdgeSpace

	self flag: #deprecated.
	^ self padding > (0 @ 0)! !

!LayoutMorph methodsFor: 'accessing' stamp: 'KenD 9/4/2024 11:48:55'!
useEdgeSpace: aBoolean

	self flag: #deprecated.
	aBoolean 
		ifTrue: [ self padding: (self xSeparation @ self ySeparation) ]
		ifFalse: [self padding: (0 at 0)].
	self layoutSubmorphs 
! !

!LayoutMorph methodsFor: 'layout' stamp: 'KenD 9/3/2024 16:22:52'!
layoutBounds
	"Return the bounds for laying out children of the receiver"

	^ self localBounds insetBy: (borderWidth + self padding)! !

!LayoutMorph methodsFor: 'layout' stamp: 'KenD 9/3/2024 18:40:24'!
layoutSubmorphsHorizontallyIn: boundsForLayout
	"Compute a new layout based on the given layout bounds."

	| visibleSubmorphs gap gapCount widths widthToAllocate leftOver x height y cache |
	"Preconditions: self isRow & morphExtent >= minimumLayoutExtent"
	(visibleSubmorphs := self submorphsToLayout reversed  "Display Order")
		ifEmpty: [ ^ self. "Nothing to layout, besides `sum` below, would fail" ].
	
	gap := self xSeparation.
	gapCount := visibleSubmorphs size - 1. "gap between each contained Morph"
	widthToAllocate := boundsForLayout width - (gapCount * gap).
	cache := IdentityDictionary new.
	widths := self widthsFor: visibleSubmorphs within: widthToAllocate minLayoutExtentCache: cache.
	leftOver := widthToAllocate - widths sum.
	x := boundsForLayout left + (leftOver * self axisEdgeWeight).

	visibleSubmorphs with: widths do: [ :sm :smWidth |
		height := self offAxisHeightFor: sm within: boundsForLayout height minLayoutExtentCache: cache.
		y := self offAxisYOffsetFor: sm within: boundsForLayout height - height.
		sm fitInto: (x @ (boundsForLayout top + y) extent: smWidth @ height).
		x := x + smWidth + gap.
	]! !

!LayoutMorph methodsFor: 'layout' stamp: 'KenD 9/3/2024 18:52:11'!
layoutSubmorphsVerticallyIn: boundsForLayout
	"Compute a new layout based on the given layout bounds."

	| visibleSubmorphs gap gapCount heights heightToAllocate leftOver y width x cache |
	"Preconditions: self isRow & morphExtent >= minimumLayoutExtent"
	(visibleSubmorphs := self submorphsToLayout reversed  "Display Order")
		ifEmpty: [ ^ self. "Nothing to layout, besides `sum` below, would fail" ].

	gap := self ySeparation.
	gapCount := visibleSubmorphs size - 1. "gap between each contained Morph"
	heightToAllocate := boundsForLayout height - (gapCount * gap).
	cache := IdentityDictionary new.
	heights := self heightsFor: visibleSubmorphs within: heightToAllocate minLayoutExtentCache: cache.
	leftOver := heightToAllocate - heights sum.
	y := boundsForLayout top + (leftOver * self axisEdgeWeight).

	visibleSubmorphs with: heights do: [ :sm :smHeight |
		width := self offAxisWidthFor: sm within: boundsForLayout width minLayoutExtentCache: cache.
		x := self offAxisXOffsetFor: sm within: boundsForLayout width - width.
		sm fitInto: (boundsForLayout left + x @ y extent: width @ smHeight).
		y := y + smHeight + gap.
	]! !

!LayoutMorph methodsFor: 'layout' stamp: 'KenD 9/3/2024 18:45:27'!
offAxisHeightFor: aMorph within: availableHeight minLayoutExtentCache: cache
	"Answer height for a single morph -- offAxis calculation for a Row"
	
	^(availableHeight * aMorph layoutSpec proportionalLayoutHeight)
							 max: (aMorph minimumLayoutExtent: cache) y.! !

!LayoutMorph methodsFor: 'layout' stamp: 'KenD 9/3/2024 18:45:59'!
offAxisWidthFor: aMorph within: availableWidth minLayoutExtentCache: cache
	"Answer width for a single morph -- offAxis calculation for a Column"

	^ (availableWidth * aMorph layoutSpec proportionalLayoutWidth)
						 	max: (aMorph minimumLayoutExtent: cache) x.
! !

!LayoutMorph methodsFor: 'layout' stamp: 'KenD 9/3/2024 18:48:57'!
offAxisXOffsetFor: aMorph within: availableWidth
	"Answer x offset for a single morph -- offAxis calculation for a Column"

	^ availableWidth * aMorph layoutSpec offAxisEdgeWeight. ! !

!LayoutMorph methodsFor: 'layout' stamp: 'KenD 9/3/2024 18:48:36'!
offAxisYOffsetFor: aMorph within: availableHeight
	"Answer y offset for a single morph -- offAxis calculation for a Row"
	
	^ availableHeight * aMorph layoutSpec offAxisEdgeWeight! !


!LayoutMorph class methodsFor: 'examples' stamp: 'KenD 9/3/2024 16:00:52'!
exampleEdgesColumn
	"Show 2 cases :1 useEdgeSpace true and 1 false"
	" self exampleEdgesColumn "
	| withEdge noEdge upSpec downSpec |
	withEdge := self newColumn :: padding: (20 @ 20);  separation: 20.
	noEdge := self newColumn ::  padding: (0 @ 0);  separation: 20.
	upSpec := LayoutSpec keepMorphExtent :: offAxisEdgeWeight: 0.
	upSpec proportionalWidth: 0.8 minimum: 100; proportionalHeight: 0.8 minimum: 100.
	downSpec := LayoutSpec keepMorphExtent :: offAxisEdgeWeight: 1.
	downSpec proportionalWidth: 0.5 minimum: 100; proportionalHeight: 0.5 minimum: 100.
	
	withEdge addMorph: (BoxedMorph new :: color: Color blue;  layoutSpec: upSpec).
	noEdge addMorph: (BoxedMorph new :: color: Color blue;  layoutSpec: upSpec).
	withEdge addMorph: (LabelMorph new :: contents: 'WITH Padding').
	noEdge addMorph: (LabelMorph new :: contents: 'withOUT Padding').
	withEdge addMorph: (BoxedMorph new :: layoutSpec: downSpec).
	noEdge addMorph: (BoxedMorph new ::  layoutSpec: downSpec).
	
	withEdge openInWorld.
	noEdge openInWorld.! !

!LayoutMorph class methodsFor: 'examples' stamp: 'KenD 9/3/2024 16:01:46'!
exampleEdgesRow
	"Show 2 cases: 1 useEdgeSpace true and 1 false"
	" self exampleEdgesRow "
	| withEdge noEdge upSpec downSpec |
	withEdge := self newRow :: padding: (20 @ 20);  separation: 20.
	noEdge := self newRow :: padding: (0 @ 0);  separation: 20.
	upSpec := LayoutSpec keepMorphExtent :: offAxisEdgeWeight: 0.
	upSpec proportionalWidth: 0.8 minimum: 100; proportionalHeight: 0.8 minimum: 100.
	downSpec := LayoutSpec keepMorphExtent :: offAxisEdgeWeight: 1.
	downSpec proportionalWidth: 0.5 minimum: 100; proportionalHeight: 0.5 minimum: 100.
	
	withEdge addMorph: (BoxedMorph new :: color: Color blue;  layoutSpec: upSpec).
	noEdge addMorph: (BoxedMorph new :: color: Color blue;  layoutSpec: upSpec).
	withEdge addMorph: (LabelMorph new :: contents: 'WITH Padding').
	noEdge addMorph: (LabelMorph new :: contents: 'withOUT Padding').
	withEdge addMorph: (BoxedMorph new :: layoutSpec: downSpec).
	noEdge addMorph: (BoxedMorph new ::  layoutSpec: downSpec).
	
	withEdge openInWorld.
	noEdge openInWorld.! !


!ImageMorph methodsFor: 'drawing' stamp: 'KenD 9/4/2024 11:59:08'!
drawOn: aCanvas

	aCanvas image: image at: self interiorOrigin.
	(borderWidth > 0) ifTrue: [
		aCanvas
			frameRectangle:  (`0 at 0` extent: extent)
			color:  borderColor
			borderWidth:  borderWidth 
			borderStyleSymbol:  #simple ]! !

!ImageMorph methodsFor: 'drawing' stamp: 'KenD 9/4/2024 12:00:06'!
minimumExtent
	^image extent + self extentBorder + (2 * self padding)! !


!InnerTextMorph methodsFor: 'drawing' stamp: 'KenD 9/4/2024 12:02:24'!
drawOn: aCanvas
	"Draw the receiver on a canvas"

	false ifTrue: [ self debugDrawLineRectsOn: aCanvas ].  "show line rects for debugging"

	aCanvas
		drawTextComposition: self textComposition
		at: self interiorOrigin
		extent: extent
		color: color
		selectionColor: (Theme current textHighlightFocused: self hasKeyboardFocus).

	model actualContents isEmpty ifTrue: [
		owner
			valueOfProperty: #emptyTextDisplayMessage
			ifPresentDo: [ :msg |
				aCanvas
					drawString: msg
					at: self interiorOrigin
					font: nil
					color: Theme current textEmptyDisplayMessage ]].! !


!LabelMorph methodsFor: 'drawing' stamp: 'KenD 9/4/2024 12:03:05'!
drawOn: aCanvas
	aCanvas
		drawString: (contents ifNil: [ '' ])
		at: self interiorOrigin
		font: self fontToUse
		color: color.
	(borderWidth > 0) ifTrue: [
		aCanvas
			frameRectangle: (`0 at 0` extent: extent)
			color: borderColor
			borderWidth: borderWidth
			borderStyleSymbol: #simple ]! !

!LabelMorph methodsFor: 'geometry' stamp: 'KenD 9/4/2024 12:05:18'!
minimumExtent

	^ self measureContents + (2 * self interiorOrigin)! !


!TextParagraphMorph methodsFor: 'drawing' stamp: 'KenD 9/4/2024 12:10:45'!
drawOn: aCanvas
	| b |
	b := self localBounds insetBy: self interiorOrigin.
	aCanvas
		fillRectangle: self localBounds color: color.
	aCanvas
		drawTextComposition: textComposition
		at: b topLeft
		extent: b extent
		color: Theme current text
		selectionColor: `Color red`
		avoidOverhang: true.
	aCanvas
		frameRectangle: self localBounds
		color: borderColor
		borderWidth: borderWidth
		borderStyleSymbol: nil! !

!TextParagraphMorph methodsFor: 'geometry' stamp: 'KenD 9/4/2024 12:11:43'!
minimumExtent

	^3 at 1 * FontFamily defaultLineSpacing ceiling + 2 + (2 *  self interiorOrigin)! !


!BoxedMorph reorganize!
('accessing' borderColor borderColor: borderWidth borderWidth: color color: padding padding:)
('initialization' defaultBorderColor defaultBorderWidth defaultColor initialize noBorder)
('drawing' drawOn:)
('geometry interior' interiorExtent interiorOrigin)
('geometry' ensureMinimumExtent extentBorder extentInOwner: extentInWorld extentInWorld: fitInto: fullBoundsInOwner heightInOwner: localBounds localBoundsForError morphBottomLeft morphBottomRight morphExtent morphExtent: morphExtentInOwner: morphExtentInWorld morphExtentInWorld: morphHeight morphHeight: morphHeightInOwner: morphLocalBounds morphPosition:extent: morphTopLeft morphTopRight morphWidth morphWidth: morphWidthInOwner: position:extent: privateExtent: rotationCenter topLeftInOwner widthInOwner:)
('geometry services' coversLocalPoint: coversPixel: includesPixel:)
('geometry testing' hasVariableExtent knowsOwnLocalBounds requiresVectorCanvas submorphsMightProtrude wantsContour)
('layout' minItemWidth)
('layout-properties' layoutSpec)
('testing' is:)
('halos and balloon help' haloShowsCoordinateSystem okayToResizeEasily okayToRotateEasily okayToScaleEasily)
('window like behavior' fullScreen resize:)
!



More information about the Cuis-dev mailing list