[squeak-dev] The Trunk: Graphics-mt.483.mcz

commits at source.squeak.org commits at source.squeak.org
Mon Feb 21 14:54:00 UTC 2022

Marcel Taeumel uploaded a new version of Graphics to project The Trunk:

==================== 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>
  	- the distance between top of line and the base line (that is the bottom of latin characters abcdehiklmnorstuvwx in most fonts)
  	- 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.
  	- 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.
  	- the total line height from top to bottom, including inter-line spacing.
  	- the line height at last encountered space or other breakable character.
  	See baselineAtSpace for explanation.
  	- the index of character after the last line break that was encountered.
  	- the index of last space or other breakable character that was encountered
  	- 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') -----
  	"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') -----
  	^ self defaultFont familyName!

Item was changed:
+ ----- Method: TextStyle>>defaultFont (in category 'accessing - default font') -----
- ----- Method: TextStyle>>defaultFont (in category 'accessing') -----
  	^ fontArray at: self defaultFontIndex!

Item was changed:
+ ----- Method: TextStyle>>defaultFontIndex (in category 'accessing - default font') -----
- ----- Method: TextStyle>>defaultFontIndex (in category 'default font') -----
  	^ 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') -----
  	| 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 (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') -----
  	"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.!

More information about the Squeak-dev mailing list