[Pkg] The Trunk: Graphics-nice.243.mcz

commits at source.squeak.org commits at source.squeak.org
Wed Oct 2 00:49:05 UTC 2013


Nicolas Cellier uploaded a new version of Graphics to project The Trunk:
http://source.squeak.org/trunk/Graphics-nice.243.mcz

==================== Summary ====================

Name: Graphics-nice.243
Author: nice
Time: 2 October 2013, 2:47:50.925 am
UUID: 220dcb9f-1fed-4f7d-b6de-170fe2b2a3ce
Ancestors: Graphics-nice.242

Fix a composition glitch when the last character of a text is a space that crosses the right margin boundary.
In such case, a virtual empty line must be added to the composition in order to correctly materialize text selection and cursor position, and so as to continue typing on next line.

The case when last character is a carriage return is in all point similar.
Indeed, a space that crossedX is visually turned into a new line.
TextComposer previously tried to reverse engineer scanner's work to recognize the CR case, which is a smell.
This change unifies handling for the two cases by rather asking to the scanner doesTheLineBreaksAfterLastChar?
Remove fixupLastLineIfCR which is tainted with half case only.
Remove the workaround in CharacterBlockScanner that did not work around anything.

Fix the breaking at non space for eastern asia:
1) registerBreakableIndex records that the line can wrap before the current character, and spaceIndex was pointing at this character that will potentially wrap on next line.
2) It is still possible to apply Justified alignment based on space adjustment if some spaces are used in the text, so correctly set the line spaceCount and paddingWidth.

=============== Diff against Graphics-nice.242 ===============

Item was changed:
  ----- Method: CharacterBlockScanner>>crossedX (in category 'stop conditions') -----
  crossedX
  	"Text display has wrapping. The scanner just found a character past the x 
  	location of the cursor. We know that the cursor is pointing at a character 
  	or before one."
  
  	self retrieveLastCharacterWidth.
  	
- 	characterIndex == nil ifFalse: [
- 		"If the last character of the last line is a space,
- 		and it crosses the right margin, then locating
- 		the character block after it is impossible without this hack."
- 		characterIndex > text size ifTrue: [
- 			lastIndex := characterIndex.
- 			characterPoint := (nextLeftMargin ifNil: [leftMargin]) @ (destY + line lineHeight).
- 			^true]].
  	characterPoint x <= (destX + (lastCharacterWidth // 2))
  		ifTrue:	[characterPoint := destX @ destY.
  				^true].
  	lastIndex >= line last 
  		ifTrue:	[characterPoint := destX @ destY.
  				^true].
  
  	"Pointing past middle of a character, return the next character."
  	lastIndex := lastIndex + 1.
  	characterPoint := destX + lastCharacterWidth + kern @ destY.
  	^ true!

Item was changed:
  CharacterScanner subclass: #CompositionScanner
+ 	instanceVariableNames: 'spaceX spaceIndex lineHeight baseline lineHeightAtSpace baselineAtSpace lastBreakIsNotASpace nextIndexAfterLineBreak'
- 	instanceVariableNames: 'spaceX spaceIndex lineHeight baseline lineHeightAtSpace baselineAtSpace lastBreakIsNotASpace'
  	classVariableNames: ''
  	poolDictionaries: ''
  	category: 'Graphics-Text'!
  
  !CompositionScanner commentStamp: '<historical>' prior: 0!
  CompositionScanners are used to measure text and determine where line breaks and space padding should occur.!

Item was changed:
  ----- Method: CompositionScanner>>composeFrom:inRectangle:firstLine:leftSide:rightSide: (in category 'scanning') -----
  composeFrom: startIndex inRectangle: lineRectangle
  	firstLine: firstLine leftSide: leftSide rightSide: rightSide
  	"Answer an instance of TextLineInterval that represents the next line in the paragraph."
  	| runLength stopCondition |
  	"Set up margins"
  	leftMargin := lineRectangle left.
  	leftSide ifTrue: [leftMargin := leftMargin +
  						(firstLine ifTrue: [textStyle firstIndent]
  								ifFalse: [textStyle restIndent])].
  	destX := spaceX := leftMargin.
  	rightMargin := lineRectangle right.
  	rightSide ifTrue: [rightMargin := rightMargin - textStyle rightIndent].
  	lastIndex := startIndex.	"scanning sets last index"
  	destY := lineRectangle top.
  	lineHeight := baseline := 0.  "Will be increased by setFont"
  	line := (TextLine start: lastIndex stop: 0 internalSpaces: 0 paddingWidth: 0)
  				rectangle: lineRectangle.
  	self setStopConditions.	"also sets font"
  	runLength := text runLengthFor: startIndex.
  	runStopIndex := (lastIndex := startIndex) + (runLength - 1).
+ 	nextIndexAfterLineBreak := spaceCount := 0.
- 	spaceCount := 0.
  	lastBreakIsNotASpace := false.
  	self handleIndentation.
  	leftMargin := destX.
  	line leftMargin: leftMargin.
  
  	[stopCondition := self scanCharactersFrom: lastIndex to: runStopIndex
  		in: text string rightX: rightMargin stopConditions: stopConditions
  		kern: kern.
  	"See setStopConditions for stopping conditions for composing."
  	self perform: stopCondition] whileFalse.
  
  	^ line
  		lineHeight: lineHeight + textStyle leading
  		baseline: baseline + textStyle leading!

Item was changed:
  ----- Method: CompositionScanner>>composeLine:fromCharacterIndex:inParagraph: (in category 'scanning') -----
  composeLine: lineIndex fromCharacterIndex: startIndex inParagraph: aParagraph 
  	"Answer an instance of TextLineInterval that represents the next line in the paragraph."
  	| runLength stopCondition |
  	destX := spaceX := leftMargin := aParagraph leftMarginForCompositionForLine: lineIndex.
  	destY := 0.
  	rightMargin := aParagraph rightMarginForComposition.
  	leftMargin >= rightMargin ifTrue: [self error: 'No room between margins to compose'].
  	lastIndex := startIndex.	"scanning sets last index"
  	lineHeight := textStyle lineGrid.  "may be increased by setFont:..."
  	baseline := textStyle baseline.
  	self setStopConditions.	"also sets font"
  	self handleIndentation.
  	runLength := text runLengthFor: startIndex.
  	runStopIndex := (lastIndex := startIndex) + (runLength - 1).
  	line := TextLineInterval
  		start: lastIndex
  		stop: 0
  		internalSpaces: 0
  		paddingWidth: 0.
+ 	nextIndexAfterLineBreak := spaceCount := 0.
- 	spaceCount := 0.
  	lastBreakIsNotASpace := false.
  	
  	[stopCondition := self scanCharactersFrom: lastIndex to: runStopIndex
  		in: text string rightX: rightMargin stopConditions: stopConditions
  		kern: kern.
  	"See setStopConditions for stopping conditions for composing."
  	self perform: stopCondition] whileFalse.
  
  	^line
  		lineHeight: lineHeight + textStyle leading
  		baseline: baseline + textStyle leading!

Item was changed:
  ----- Method: CompositionScanner>>cr (in category 'stop conditions') -----
  cr
  	"Answer true. Set up values for the text line interval currently being 
  	composed."
  
  	pendingKernX := 0.
  	(lastIndex < text size and: [(text at: lastIndex) = CR and: [(text at: lastIndex+1) = Character lf]]) ifTrue: [lastIndex := lastIndex + 1].
  	line stop: lastIndex.
+ 	nextIndexAfterLineBreak := lastIndex + 1.
  	spaceX := destX.
  	lastBreakIsNotASpace := false.
  	line paddingWidth: rightMargin - spaceX.
  	^true!

Item was changed:
  ----- Method: CompositionScanner>>crossedX (in category 'stop conditions') -----
  crossedX
  	"There is a word that has fallen across the right edge of the composition 
  	rectangle. This signals the need for wrapping which is done to the last 
  	space that was encountered, as recorded by the space stop condition,
  	or any other breakable character if the language permits so."
  
  	pendingKernX := 0.
  	
  	lastBreakIsNotASpace ifTrue:
+ 		["In some languages break is possible before a non space."
+ 		nextIndexAfterLineBreak := spaceIndex.
+ 		line stop: spaceIndex - 1.
- 		["In some languages break is possible on non space."
- 		line stop: spaceIndex.
  		lineHeight := lineHeightAtSpace.
  		baseline := baselineAtSpace.
+ 		line paddingWidth: rightMargin - spaceX.
- 		spaceCount := spaceCount - 1.
- 		spaceIndex := spaceIndex - 1.
- 		line paddingWidth: rightMargin.
  		line internalSpaces: spaceCount.
  		^true].
  	 
  	spaceCount >= 1 ifTrue:
  		["The common case. First back off to the space at which we wrap."
  		line stop: spaceIndex.
+ 		nextIndexAfterLineBreak := spaceIndex + 1.
  		lineHeight := lineHeightAtSpace.
  		baseline := baselineAtSpace.
  		spaceCount := spaceCount - 1.
  		spaceIndex := spaceIndex - 1.
  
  		"Check to see if any spaces preceding the one at which we wrap.
  			Double space after punctuation, most likely."
  		[(spaceCount > 1 and: [(text at: spaceIndex) = Space])]
  			whileTrue:
  				[spaceCount := spaceCount - 1.
  				"Account for backing over a run which might
  					change width of space."
  				font := text fontAt: spaceIndex withStyle: textStyle.
  				spaceIndex := spaceIndex - 1.
  				spaceX := spaceX - (font widthOf: Space)].
  		line paddingWidth: rightMargin - spaceX.
  		line internalSpaces: spaceCount]
  	ifFalse:
  		["Neither internal nor trailing spaces -- almost never happens."
  		lastIndex := lastIndex - 1.
  		[destX <= rightMargin or: [ lastIndex = 0 ]]
  			whileFalse:
  				[destX := destX - (font widthOf: (text at: lastIndex)).
  				lastIndex := lastIndex - 1].
+ 		nextIndexAfterLineBreak := lastIndex + 1.
  		spaceX := destX.
  		line paddingWidth: rightMargin - destX.
  		line stop: (lastIndex max: line first)].
  	^true!

Item was added:
+ ----- Method: CompositionScanner>>doesTheLineBreaksAfterLastChar (in category 'accessing') -----
+ doesTheLineBreaksAfterLastChar
+ 	^nextIndexAfterLineBreak > text size!

Item was changed:
  ----- Method: TextComposer>>composeLinesFrom:to:delta:into:priorLines:atY:textStyle:text:container:wantsColumnBreaks: (in category 'as yet unclassified') -----
  composeLinesFrom: argStart to: argStop delta: argDelta into: argLinesCollection priorLines: argPriorLines atY: argStartY textStyle: argTextStyle text: argText container: argContainer wantsColumnBreaks: argWantsColumnBreaks
  
  	wantsColumnBreaks := argWantsColumnBreaks.
  	lines := argLinesCollection.
  	theTextStyle := argTextStyle.
  	theText := argText.
  	theContainer := argContainer.
  	deltaCharIndex := argDelta.
  	currCharIndex := startCharIndex := argStart.
  	stopCharIndex := argStop.
  	prevLines := argPriorLines.
  	currentY := argStartY.
  	maxRightX := theContainer left.
  	possibleSlide := stopCharIndex < theText size and: [theContainer isMemberOf: Rectangle].
  	nowSliding := false.
  	prevIndex := 1.
  	"choose an appropriate scanner - should go away soon, when they can be unified"
  	scanner := CompositionScanner new.
  	scanner text: theText textStyle: theTextStyle.
  	scanner wantsColumnBreaks: wantsColumnBreaks.
  	defaultLineHeight := scanner computeDefaultLineHeight.
  	isFirstLine := true.
  	self composeAllLines.
  	isFirstLine ifTrue: ["No space in container or empty text"
  		self 
  			addNullLineWithIndex: startCharIndex
  			andRectangle: (theContainer topLeft extent: 0 at defaultLineHeight)
  	] ifFalse: [
+ 		(lines last last = theText size and: [scanner doesTheLineBreaksAfterLastChar])
+ 			ifTrue: [self addNullLineForIndex: theText size + 1]
- 		self fixupLastLineIfCR
  	].
  	^{lines asArray. maxRightX}
- 
  !

Item was removed:
- ----- Method: TextComposer>>fixupLastLineIfCR (in category 'as yet unclassified') -----
- fixupLastLineIfCR
- "This awful bit is to ensure that if we have scanned all the text and the last character is a CR that there is a null line at the end of lines. Sometimes this was not happening which caused anomalous selections when selecting all the text. This is implemented as a post-composition fixup because I couldn't figure out where to put it in the main logic."
- 
- 	(theText size > 0 and: [CharacterSet crlf includes: theText last]) ifFalse: [^self].
- 	self addNullLineForIndex: theText size + 1.
- !

Item was removed:
- ----- Method: TextComposer>>multiComposeLinesFrom:to:delta:into:priorLines:atY:textStyle:text:container:wantsColumnBreaks: (in category 'as yet unclassified') -----
- multiComposeLinesFrom: argStart to: argStop delta: argDelta into: argLinesCollection priorLines: argPriorLines atY: argStartY textStyle: argTextStyle text: argText container: argContainer wantsColumnBreaks: argWantsColumnBreaks
- 
- "temporarily add this here to support move to drop MultiTextComposer"
- "now redundant and ready to remove later"
- 	wantsColumnBreaks := argWantsColumnBreaks.
- 	lines := argLinesCollection.
- 	theTextStyle := argTextStyle.
- 	theText := argText.
- 	theContainer := argContainer.
- 	deltaCharIndex := argDelta.
- 	currCharIndex := startCharIndex := argStart.
- 	stopCharIndex := argStop.
- 	prevLines := argPriorLines.
- 	currentY := argStartY.
- 	maxRightX := theContainer left.
- 	possibleSlide := stopCharIndex < theText size and: [theContainer isMemberOf: Rectangle].
- 	nowSliding := false.
- 	prevIndex := 1.
- 	scanner := CompositionScanner new text: theText textStyle: theTextStyle.
- 	scanner wantsColumnBreaks: wantsColumnBreaks.
- 	defaultLineHeight := scanner computeDefaultLineHeight.
- 	isFirstLine := true.
- 	self composeAllLines.
- 	isFirstLine ifTrue: ["No space in container or empty text"
- 		self
- 			addNullLineWithIndex: startCharIndex
- 			andRectangle: (theContainer topLeft extent: 0 at defaultLineHeight)
- 	] ifFalse: [
- 		self fixupLastLineIfCR
- 	].
- 	^{lines asArray. maxRightX}
- 
- !



More information about the Packages mailing list