Marcel Taeumel uploaded a new version of Graphics to project The Trunk:
http://source.squeak.org/trunk/Graphics-mt.483.mcz
==================== Summary ====================
Name: Graphics-mt.483
Author: mt
Time: 21 February 2022, 3:53:52.677099 pm
UUID: 90c68629-6678-8f46-9494-b74dad8c28b1
Ancestors: Graphics-mt.482
Adds (class) commentary to TextStyle and does some clean up:
- Remove the recently added #leadingSlice(:) again as was already replaced with the font's #lineGapSlice
- Deprecate absolute #leading(:) and replace it with the relative #lineSpacing measure for which TextLine gets #topMargin and #bottomMargin
#lineSpacing not yet active because I want to add an extra checkpoint before updating TextComposer and CompositionScanner. Just to be sure.
=============== Diff against Graphics-mt.482 ===============
Item was changed:
CharacterScanner subclass: #CompositionScanner
+ instanceVariableNames: 'spaceX spaceIndex lineHeight baseline lineGap lineGapSlice topMargin bottomMargin lineHeightAtSpace baselineAtSpace lastBreakIsNotASpace nextIndexAfterLineBreak'
- instanceVariableNames: 'spaceX spaceIndex lineHeight baseline lineGap lineGapSlice lineHeightAtSpace baselineAtSpace lastBreakIsNotASpace nextIndexAfterLineBreak'
classVariableNames: ''
poolDictionaries: ''
category: 'Graphics-Text'!
!CompositionScanner commentStamp: 'nice 10/6/2013 23:24' prior: 0!
A CompositionScanner measures text and determines where line breaks.
Given a rectangular zone on input, it is used to split text in horizontal lines, and produce information about those lines on output (at which index a line starts/stops, which vertical space does the line require, which horizontal space if left for adjusting inter-word spacing, etc...)
Instance Variables
baseline: <Number>
baselineAtSpace: <Number>
lastBreakIsNotASpace: <Boolean>
lineHeight: <Number>
lineHeightAtSpace: <Number>
nextIndexAfterLineBreak: <Integer>
spaceIndex: <Integer>
spaceX: <Number>
baseline
- the distance between top of line and the base line (that is the bottom of latin characters abcdehiklmnorstuvwx in most fonts)
baselineAtSpace
- memorize the baseline at last encountered space or other breakable character.
This is necessary because the CompositionScanner wants to break line at a breakable character.
If a word layout overflows the right margin, the scanner has to roll back and restore the line state to last encountered breakable character.
lastBreakIsNotASpace
- indicates that the last breakable character was not a space.
This is necessary because handling a line break at a space differs from non space.
If line break occurs on space, the space won't be displayed in next line.
If it's another breakable character, it has to be displayed on next line.
lineHeight
- the total line height from top to bottom, including inter-line spacing.
lineHeightAtSpace
- the line height at last encountered space or other breakable character.
See baselineAtSpace for explanation.
nextIndexAfterLineBreak
- the index of character after the last line break that was encountered.
spaceIndex
- the index of last space or other breakable character that was encountered
spaceX
- the distance from left of composition zone to left of last encountered space or other breakable character
See baselineAtSpace for explanation.
Note: if a line breaks on a space, a linefeed or a carriage return, then the space, linefeed or carriage return is integrated in the line.
If there is a carriage return - linefeed pair, the pair is integrated to the line as if it were a single line break for compatibility with legacy software.!
Item was changed:
Object subclass: #TextLine
+ instanceVariableNames: 'left right top bottom firstIndex lastIndex internalSpaces paddingWidth baseline leftMargin topMargin bottomMargin'
- instanceVariableNames: 'left right top bottom firstIndex lastIndex internalSpaces paddingWidth baseline leftMargin'
classVariableNames: ''
poolDictionaries: 'TextConstants'
category: 'Graphics-Text'!
!TextLine commentStamp: '<historical>' prior: 0!
A TextLine embodies the layout of a line of composed text.
left right top bottom The full line rectangle
firstIndex lastIndex Starting and stopping indices in the full text
internalSpaces Number of spaces to share paddingWidth
paddingWidth Number of pixels of extra space in full line
baseline Distance of baseline below the top of the line
leftMargin Left margin due to paragraph indentation
TextLine's rather verbose message protocol is required for compatibility with the old CharacterScanners.!
Item was added:
+ ----- Method: TextLine>>bottomMargin (in category 'accessing') -----
+ bottomMargin
+
+ ^ bottomMargin!
Item was added:
+ ----- Method: TextLine>>lineHeightWithMargins (in category 'accessing') -----
+ lineHeightWithMargins
+
+ ^ bottom - top + topMargin + bottomMargin!
Item was added:
+ ----- Method: TextLine>>moveByTopMargin (in category 'updating') -----
+ moveByTopMargin
+
+ top := top + topMargin.
+ bottom := bottom + topMargin.
+ !
Item was added:
+ ----- Method: TextLine>>topMargin (in category 'accessing') -----
+ topMargin
+
+ ^ topMargin!
Item was added:
+ ----- Method: TextLine>>topMargin:bottomMargin: (in category 'private') -----
+ topMargin: tm bottomMargin: bm
+ topMargin := tm.
+ bottomMargin := bm.!
Item was changed:
Object subclass: #TextStyle
+ instanceVariableNames: 'fontArray alignment firstIndent restIndent rightIndent tabsArray marginTabsArray defaultFontIndex lineSpacing'
- instanceVariableNames: 'fontArray fontFamilySize lineGrid baseline alignment firstIndent restIndent rightIndent tabsArray marginTabsArray leading leadingSlice defaultFontIndex'
classVariableNames: 'NumSpacesPerTab'
poolDictionaries: 'TextConstants'
category: 'Graphics-Text'!
+ !TextStyle commentStamp: 'mt 2/21/2022 10:55' prior: 0!
+ A text style comprises the formatting information for composing and displaying a unit (usually a paragraph) of text. It provides a #defaultFont to use, but text attributes can change that per character (see CompositionScanner and DisplayScanner). It also has a default #alignment that attributes can override. Those defaults make it possible to compose and display (unformatted) strings in paragraphs without having to style them first as texts (i.e., string+attributes).
- !TextStyle commentStamp: '<historical>' prior: 0!
- A textStyle comprises the formatting information for composing and displaying a unit (usually a paragraph) of text. Typically one makes a copy of a master textStyle (such as TextStyle default), and then that copy may get altered in the process of editing. Bad things can happen if you do not copy first.
+ NOTE THAT for each use you *must* make a copy of a font's master text style (e.g., "TextStyle default copy") or create a fresh one with at least a single font (see TextStyle class >> #fontArray: and AbstractFont >> #asNewTextStyle). That specific instance is typically altered in the process of editing: change default font size, change default alignment, ... and you wouldn't want to change that properties for other applications by accident.
- Each of my instances consists of...
- fontArray An array of StrikeFonts
- fontFamilySize unused
- lineGrid An integer; default line spacing for paragraphs
- baseline An integer; default baseline (dist from line top to bottom of an 'a')
- alignment An integer; text alignment, see TextStyle alignment:
- firstIndent An integer; indent of first line in pixels
- restIndent An integer; indent of remaining lines in pixels
- rightIndent An integer; indent of right margin rel to section
- tabsArray An array of integers giving tab offsets in pixels
- marginTabsArray An array of margin tabs
- leading An integer giving default vertical line separation
+ A text style also drives the interpretation of Character tab. Both tabsArray and marginTabsArray are initialized for the #defaultFont(Index:). When you change a style's default font size, those "tab positions" will be recomputed for fast access during composition. See the preference #numSpacesPerTab(:).
+
+ While each text style looks like it could handle an arbitrary array of fonts, it is *best practice* to only store fonts of the same font family. A font's master style thus collects all known point sizes at a single place (i.e. "TextStyle named: aFamilyName"). Copies will share that array. The attribute TextFontChange makes it possible to switch to any index in that array, but this is not portable and hence discouraged. TextFontReference adds an explicit reference to font, which is also not good. (February 2022: We plan to add TextFont(Point)Size and TextFontFamily as a portable way to change the font per character.).
+
+ There are some legacy information, which should no longer be used:
+ - #baseline: ... used to prescribe baseline info but is now completely derived from #defaultFont
+ - #lineGrid: ... same as #baseLine:
+ - #leading(:) ... is replaced by #lineSpacing(:) and denotes the extra spacing relative to the respective line's height in the composition
+
+ The #lineSpacing is noticeable in a paragraph's text selection. Line spacing < 0.0 will appear as overlaps between (translucent) selection rectangles. Lince spacing > 0.0 will appear as gaps between selection rectangles.
+
+ Here are some example styles to explore:
+ - TextStyle default
+ - TextStyle defaultFixes!
- For a concrete example, look at TextStyle default copy inspect!
Item was changed:
+ ----- Method: TextStyle>>baseline (in category 'accessing - default font') -----
- ----- Method: TextStyle>>baseline (in category 'accessing') -----
baseline
"Answer the distance from the top of the line to the bottom of most of the
characters (by convention, bottom of the letter 'A')."
+ ^ self defaultFont ascent!
- ^baseline!
Item was changed:
+ ----- Method: TextStyle>>baseline: (in category 'accessing - default font') -----
- ----- Method: TextStyle>>baseline: (in category 'accessing') -----
baseline: anInteger
- "Set the distance from the top of the line to the bottom of most of the
- characters."
+ self flag: #deprecated. "Either change #defaultFont in this style or use custom fonts via text attributes."!
- baseline := anInteger!
Item was changed:
+ ----- Method: TextStyle>>defaultFamilyName (in category 'accessing - default font') -----
- ----- Method: TextStyle>>defaultFamilyName (in category 'accessing') -----
defaultFamilyName
^ self defaultFont familyName!
Item was changed:
+ ----- Method: TextStyle>>defaultFont (in category 'accessing - default font') -----
- ----- Method: TextStyle>>defaultFont (in category 'accessing') -----
defaultFont
^ fontArray at: self defaultFontIndex!
Item was changed:
+ ----- Method: TextStyle>>defaultFontIndex (in category 'accessing - default font') -----
- ----- Method: TextStyle>>defaultFontIndex (in category 'default font') -----
defaultFontIndex
^ defaultFontIndex ifNil: [defaultFontIndex := 1]!
Item was changed:
+ ----- Method: TextStyle>>defaultFontIndex: (in category 'accessing - default font') -----
- ----- Method: TextStyle>>defaultFontIndex: (in category 'default font') -----
defaultFontIndex: anIndex
defaultFontIndex := anIndex.
-
- leading := self defaultFont lineGap.
- leadingSlice := self defaultFont lineGapSlice.
- lineGrid := self defaultFont height + leading.
- baseline := self defaultFont ascent + leadingSlice.
-
self initializeTabsArray.!
Item was changed:
----- Method: TextStyle>>gridForFont:withLead: (in category 'private') -----
gridForFont: fontIndex withLead: leadInteger
"Force whole style to suit one of its fonts. Assumes only one font referred
to by runs."
+
+ self flag: #deprecated.
+ self defaultFontIndex: fontIndex.!
- | font |
- font := self fontAt: fontIndex.
- self lineGrid: font height + leadInteger.
- self baseline: font ascent.
- self leading: leadInteger!
Item was removed:
- ----- Method: TextStyle>>initialize (in category 'initialize-release') -----
- initialize
-
- super initialize.
- self leading: 2.
- self leadingSlice: 1.!
Item was changed:
----- Method: TextStyle>>initializeTabsArray (in category 'initialize-release') -----
initializeTabsArray
| fontToUse numSpacesPerTab tabWidth maxWidth |
self flag: #discuss. "mt: Add cache per font and pointSize? Maybe it is not worth it..."
numSpacesPerTab := self class numSpacesPerTab.
fontToUse := self defaultFont.
maxWidth := Display width max: 3840.
+ tabWidth := ((fontToUse widthOf: Character space) max: 1 "For tiny point sizes...") * numSpacesPerTab.
- tabWidth := (fontToUse widthOf: Character space) * numSpacesPerTab.
"Note that using Interval via #to:by: and #asArray would be about 4x slower."
tabsArray := Array new: maxWidth // tabWidth.
1 to: tabsArray size do: [:i | tabsArray at: i put: tabWidth * i].
marginTabsArray := Array new: (maxWidth // tabWidth) // 2.
1 to: marginTabsArray size do: [:i | | offset |
marginTabsArray at: i put: (Array with: (offset := tabWidth * i) with: offset)].!
Item was changed:
+ ----- Method: TextStyle>>leading (in category 'accessing - default font') -----
- ----- Method: TextStyle>>leading (in category 'accessing') -----
leading
"Leading (from typographers historical use of extra lead (type metal))
is the extra spacing above and beyond that needed just to accomodate
the various font heights in the set."
+
+ self flag: #deprecated. "mt: Fonts provide their #lineGap (and #lineGapSlice) in the CompositionScanner to accommodate various font heights in a set. Use #lineSpacing to define a factor that moves the lines further apart."
+ ^ 0!
- ^ leading!
Item was changed:
+ ----- Method: TextStyle>>leading: (in category 'accessing - default font') -----
- ----- Method: TextStyle>>leading: (in category 'accessing') -----
leading: yDelta
+ self flag: #deprecated. "See commentary in #leading."!
- leading := yDelta!
Item was removed:
- ----- Method: TextStyle>>leadingSlice (in category 'accessing') -----
- leadingSlice
- "Cached portion of the receiver's #leading, which can be used to vertically center one-liners in text fields."
-
- ^ leadingSlice!
Item was removed:
- ----- Method: TextStyle>>leadingSlice: (in category 'accessing') -----
- leadingSlice: yDeltaSlice
-
- leadingSlice := yDeltaSlice.!
Item was changed:
+ ----- Method: TextStyle>>lineGrid (in category 'accessing - default font') -----
- ----- Method: TextStyle>>lineGrid (in category 'accessing') -----
lineGrid
"Answer the relative space between lines of a paragraph in the style of
the receiver."
+ ^ self defaultFont lineGrid!
- ^lineGrid!
Item was changed:
+ ----- Method: TextStyle>>lineGrid: (in category 'accessing - default font') -----
- ----- Method: TextStyle>>lineGrid: (in category 'accessing') -----
lineGrid: anInteger
- "Set the relative space between lines of a paragraph in the style of the
- receiver to be the argument, anInteger."
+ self flag: #deprecated. "Either change #defaultFont in this style or use custom fonts via text attributes."!
- lineGrid := anInteger!
Item was added:
+ ----- Method: TextStyle>>lineSpacing (in category 'accessing') -----
+ lineSpacing
+ "Answer the factor that is used to compute extra spacing between text lines. The default is 0.0, which means that the CompositionScanner will just rely on the various font metrics in a line. There will be 0% of extra spacing. Use the current line height as blank space with a factor of 1.0 and so on (i.e. the common misnomer 'double line space')."
+
+ ^ lineSpacing ifNil: [0.0]!
Item was added:
+ ----- Method: TextStyle>>lineSpacing: (in category 'accessing') -----
+ lineSpacing: aFactor
+
+ lineSpacing := aFactor.!
Marcel Taeumel uploaded a new version of Morphic to project The Trunk:
http://source.squeak.org/trunk/Morphic-mt.1903.mcz
==================== Summary ====================
Name: Morphic-mt.1903
Author: mt
Time: 21 February 2022, 3:47:13.652099 pm
UUID: 3bdc0c37-abfd-ae40-9040-42769b8b3683
Ancestors: Morphic-mt.1902
It is not necessary to copy a text style during text composition. The text style will not be modified during composition. It must be a copy already when the surrounding text morph was configured.
=============== Diff against Morphic-mt.1902 ===============
Item was changed:
----- Method: TextMorph>>createParagraph (in category 'private') -----
createParagraph
self setProperty: #CreatingParagraph toValue: true.
[
self setDefaultContentsIfNil.
"...Code here to recreate the paragraph..."
paragraph := (self paragraphClass new textOwner: self owner).
paragraph wantsColumnBreaks: successor notNil.
paragraph
compose: text
+ style: textStyle
- style: textStyle copy
from: self startingIndex
in: self container.
wrapFlag ifFalse:
["Was given huge container at first... now adjust"
paragraph adjustRightX].
paragraph focused: (self currentHand keyboardFocus == self).
paragraph
caretColor: self caretColor;
selectionColor: self selectionColor;
unfocusedSelectionColor: self unfocusedSelectionColor.
self fit.
] ensure: [self removeProperty: #CreatingParagraph].
^ paragraph!
Marcel Taeumel uploaded a new version of Morphic to project The Trunk:
http://source.squeak.org/trunk/Morphic-mt.1902.mcz
==================== Summary ====================
Name: Morphic-mt.1902
Author: mt
Time: 21 February 2022, 9:31:41.263853 am
UUID: 6fc55666-eb72-8240-8d39-22e44c25d2fc
Ancestors: Morphic-ct.1901
Adds commentary about the current situation of a morph's drop-shadow cache.
Thanks to Christoph (ct) for pointing this out!
=============== Diff against Morphic-ct.1901 ===============
Item was changed:
----- Method: Morph>>updateDropShadowCache (in category 'drawing') -----
updateDropShadowCache
+ "Draws the receiver's drop shadow into a separate form (or cache) to be used repeatedly in #drawDropShadowOn:, which is itself guarded via #hasDropShadow (see #fullDrawOn:).
+
+ Note that this cache is not so much about performance as it is about visual aesthetics. While the shadow itself is just one or more repeated calls to fill/frame a (rounded rectangle), we finally cut out (or mask or erase) the inner portion so that translucent receiver's wont look awkward. This is not possible with direct drawing calls to BitBlt onto Display.
+
+ Also note that with the advent of the Spur object memory (http://www.mirandabanda.org/cogblog/category/spur/) in the OpenSmalltalk VM, we got a different garbage collector (GC) that does not yet have the most efficient incremental collection strategies. As an effect, repeated invalidation of the drop-shadow cache now entails frequent full-GC pauses and thus noticeable lags (or stuttering) in the environment. This has been the case since the release of Squeak 5.0, where we started to use the Spur object memory and hence the new GC.
+
+ To make the full-GC pauses less noticeable, we started to temporarily disable the drop shadow in situations where responsiveness is importent. For example, we do this for frequently used morphs such as all system windows when being resized using their corner (or edge) grips. You can get an overview of thus points by browsing senders of #targetHadDropShadow and #hasDropShadow: (or actually the code 'hasDropShadow: false', which typically starts the temporary disabling of the shadow).
+
+ February 2022: We are currently working on improving the incremental compaction in the OpenSmalltalk VM. Once that issue has been solved, we can remove that source code that disables the drop shadow temporarily."
| shadowOffset shadowBounds offset form canvas drawBlock localBounds mask maskCanvas |
+ self flag: #hasDropShadow. "Marker for senders browsing."
+ self flag: #targetHadDropShadow. "Marker for senders browsing."
+
(shadowOffset := self shadowOffset) isRectangle
ifTrue: [
shadowBounds := 0@0 corner: (self bounds outsetBy: shadowOffset) extent.
offset := 0@0.
localBounds := shadowOffset topLeft extent: self extent ]
ifFalse: [
| extent |
extent := self extent.
shadowBounds := 0@0 corner: extent + shadowOffset abs.
offset := shadowOffset max: 0@0.
localBounds := (shadowOffset negated max: 0@0) extent: extent ].
form := Form extent: shadowBounds extent depth: Display depth.
canvas := form getCanvas.
drawBlock := self useSoftDropShadow
ifFalse: [
[:c | self wantsRoundedCorners
ifTrue: [c fillRoundRect: localBounds radius: self cornerRadius fillStyle: self shadowColor]
ifFalse: [c fillRectangle: localBounds fillStyle: self shadowColor]]]
ifTrue: [
[:c | self wantsRoundedCorners
ifTrue: [0 to: 9 do: [:i |
c
fillRoundRect: (shadowBounds insetBy: i)
radius: (self cornerRadius max: 20) -i
fillStyle: (self shadowColor alpha: self shadowColor alpha * (i+1))]]
ifFalse: [0 to: 9 do: [:i |
c
fillRoundRect: (shadowBounds insetBy: i) radius: 20-i
fillStyle: (self shadowColor alpha: self shadowColor alpha * (i+1))]]]].
canvas
translateBy: offset
during: [ :shadowCanvas | drawBlock value: shadowCanvas].
"Support transparent morph colors without having the shadow to shine through.."
mask := Form extent: shadowBounds extent depth: Display depth.
maskCanvas := mask getCanvas.
self wantsRoundedCorners
ifTrue: [maskCanvas fillRoundRect: (localBounds insetBy: self borderWidth) radius: self cornerRadius fillStyle: Color black]
ifFalse: [maskCanvas fillRectangle: (localBounds insetBy: self borderWidth) fillStyle: Color black].
mask
displayOn: form
at: 0@0
rule: Form erase.
self setProperty: #dropShadow toValue: form.!
Christoph Thiede uploaded a new version of ToolBuilder-Kernel to project The Trunk:
http://source.squeak.org/trunk/ToolBuilder-Kernel-ct.156.mcz
==================== Summary ====================
Name: ToolBuilder-Kernel-ct.156
Author: ct
Time: 21 February 2022, 1:11:25.464369 am
UUID: 06c1b35a-e089-184c-a382-d07a3a3797e9
Ancestors: ToolBuilder-Kernel-mt.155
Adds PluggableMenuSpec>>addLine for compatibility/consistency with MenuMorph/CustomMenu.
=============== Diff against ToolBuilder-Kernel-mt.155 ===============
Item was added:
+ ----- Method: PluggableMenuSpec>>addLine (in category 'construction') -----
+ addLine
+ "For compatibility with MenuMorph/CustomMenu."
+
+ ^ self addSeparator!
David T. Lewis uploaded a new version of MonticelloConfigurations to project The Trunk:
http://source.squeak.org/trunk/MonticelloConfigurations-dtl.175.mcz
==================== Summary ====================
Name: MonticelloConfigurations-dtl.175
Author: dtl
Time: 20 February 2022, 1:35:49.994213 pm
UUID: aa6e95c8-d9d2-4791-9df8-aa7628e0dac3
Ancestors: MonticelloConfigurations-mt.174
When opening a new MCConfigurationBrowser from the world menu, initialize it with MCConfigurationExtended so that comment and author information can be saved.
=============== Diff against MonticelloConfigurations-mt.174 ===============
Item was changed:
----- Method: MCConfigurationBrowser>>configuration (in category 'accessing') -----
configuration
+ ^configuration ifNil: [configuration := MCConfigurationExtended new]!
- ^configuration ifNil: [configuration := MCConfiguration new]!
Marcel Taeumel uploaded a new version of Morphic to project The Trunk:
http://source.squeak.org/trunk/Morphic-mt.1900.mcz
==================== Summary ====================
Name: Morphic-mt.1900
Author: mt
Time: 16 February 2022, 5:08:45.790238 pm
UUID: 3be7afd2-a52e-8c48-9625-c7f69da116b5
Ancestors: Morphic-tonyg.1899
I forgot to scale the margins in text fields and lists after scale changes. Still not perfect because of rounding errors. In this case, it is a #truncated vs. #rounded thing. In this case, we use #truncated in, for example, #textMargins because we do not want to use 2px-widths for 150% or 175% scaling.
=============== Diff against Morphic-tonyg.1899 ===============
Item was added:
+ ----- Method: PluggableListMorph>>displayScaleChangedBy: (in category 'display scale') -----
+ displayScaleChangedBy: factor
+
+ | currentMargins |
+ super displayScaleChangedBy: factor.
+ (currentMargins := self listMorph cellInset) isRectangle
+ ifTrue: [self listMorph cellInset: ((currentMargins origin * factor) rounded corner: (currentMargins corner * factor) rounded)]
+ ifFalse: [self listMorph cellInset: (currentMargins * factor) rounded].!
Item was added:
+ ----- Method: PluggableTextMorph>>displayScaleChangedBy: (in category 'display scale') -----
+ displayScaleChangedBy: factor
+
+ | currentMargins |
+ super displayScaleChangedBy: factor.
+ (currentMargins := self textMorph margins) isRectangle
+ ifTrue: [self textMorph margins: ((currentMargins origin * factor) rounded corner: (currentMargins corner * factor) rounded)]
+ ifFalse: [self textMorph margins: (currentMargins * factor) rounded].!