<div dir="ltr"><div><div><div><div>I did not find regressions, but this part lacks automated tests (really).<br></div><br>Having a space crossing rightMargin on last line is not handled correctly despite the machinery found in crossedX...<br>
But is wasn't before those changes...<br></div><div>Though, a MVC Paragraph makes a better job in this case...<br><br></div>I can't understand why theFirstCharCrossedX is required, nor this section of endOfRun:<br>
characterIndex ~~ nil<br> ifTrue: ["If scanning for an index and we've stopped on that index,<br> then we back destX off by the width of the character stopped on<br> (it will be pointing at the right side of the character) and return"<br>
runStopIndex = characterIndex<br> ifTrue: [characterPoint := destX - lastCharacterWidth @ destY.<br> ^true].<br></div>They might be related, but not sure...<br>
<br></div>rightFlush still is messy like it was, but it might be at composition stage too.<br></div><div class="gmail_extra"><br><br><div class="gmail_quote">2013/10/1 <span dir="ltr"><<a href="mailto:commits@source.squeak.org" target="_blank">commits@source.squeak.org</a>></span><br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Nicolas Cellier uploaded a new version of Graphics to project The Trunk:<br>
<a href="http://source.squeak.org/trunk/Graphics-nice.242.mcz" target="_blank">http://source.squeak.org/trunk/Graphics-nice.242.mcz</a><br>
<br>
==================== Summary ====================<br>
<br>
Name: Graphics-nice.242<br>
Author: nice<br>
Time: 1 October 2013, 2:39:06.884 am<br>
UUID: ca886e46-dd2e-448d-a639-6cf5060948aa<br>
Ancestors: Graphics-tpr.241<br>
<br>
More simplification of CharacterBlockScanner<br>
1) Replace lastCharacterExtent with lastCharacterWidth : more simple.<br>
Thus we can remove lastCharacterExtentSetX:<br>
2) Don't record lastCharacterExtent + lastSpaceOrTabExtent it's too much.<br>
- A stopCondition will set the lastCharacterWidth.<br>
- For any other character, this can be retrieved on demand by retrieveLastCharacterWidth.<br>
So in the end, lastCharacterWidth is all we ever wanted.<br>
Thus we can remove lastSpaceOrTabExtentSetX:<br>
3) Move the hack for click after middle of last char with his colleagues in the stopCondition.<br>
This restores some homogeneity between MVC and Morphic code.<br>
Remove Yukky code.<br>
<br>
=============== Diff against Graphics-tpr.241 ===============<br>
<br>
Item was changed:<br>
CharacterScanner subclass: #CharacterBlockScanner<br>
+ instanceVariableNames: 'characterPoint characterIndex nextLeftMargin specialWidth lastCharacterWidth'<br>
- instanceVariableNames: 'characterPoint characterIndex lastCharacterExtent lastSpaceOrTabExtent nextLeftMargin specialWidth'<br>
classVariableNames: ''<br>
poolDictionaries: ''<br>
category: 'Graphics-Text'!<br>
<br>
!CharacterBlockScanner commentStamp: '<historical>' prior: 0!<br>
My instances are used to scan text to compute the CharacterBlock for a character specified by its index in the text or its proximity to the cursor location.!<br>
<br>
Item was changed:<br>
----- Method: CharacterBlockScanner>>buildCharacterBlockIn: (in category 'private') -----<br>
buildCharacterBlockIn: para<br>
"This method is used by the MVC version only."<br>
<br>
| lineIndex runLength lineStop stopCondition |<br>
"handle nullText"<br>
(para numberOfLines = 0 or: [text size = 0])<br>
ifTrue: [^ CharacterBlock new stringIndex: 1 "like being off end of string"<br>
text: para text<br>
topLeft: (para leftMarginForDisplayForLine: 1 alignment: (alignment ifNil:[textStyle alignment]))<br>
@ para compositionRectangle top<br>
extent: 0 @ textStyle lineGrid].<br>
"find the line"<br>
lineIndex := para lineIndexOfTop: characterPoint y.<br>
destY := para topAtLineIndex: lineIndex.<br>
line := para lines at: lineIndex.<br>
lastIndex := line first.<br>
rightMargin := para rightMarginForDisplay.<br>
self setStopConditions. " also loads the font, alignment and all emphasis attributes "<br>
<br>
(lineIndex = para numberOfLines and:<br>
[(destY + line lineHeight) < characterPoint y])<br>
ifTrue: ["if beyond lastLine, force search to last character"<br>
self characterPointSetX: rightMargin]<br>
ifFalse: [characterPoint y < (para compositionRectangle) top<br>
ifTrue: ["force search to first line"<br>
characterPoint := (para compositionRectangle) topLeft].<br>
characterPoint x > rightMargin<br>
ifTrue: [self characterPointSetX: rightMargin]].<br>
destX := leftMargin := para leftMarginForDisplayForLine: lineIndex alignment: alignment.<br>
nextLeftMargin:= para leftMarginForDisplayForLine: lineIndex+1 alignment: alignment.<br>
runLength := text runLengthFor: line first.<br>
lineStop := characterIndex "scanning for index"<br>
ifNil: [ line last ]. "scanning for point"<br>
runStopIndex := lastIndex + (runLength - 1) min: lineStop.<br>
+ lastCharacterWidth := 0.<br>
- lastCharacterExtent := 0 @ line lineHeight.<br>
spaceCount := 0.<br>
self handleIndentation.<br>
<br>
[stopCondition := self scanCharactersFrom: lastIndex to: runStopIndex<br>
in: text string rightX: characterPoint x<br>
stopConditions: stopConditions kern: kern.<br>
"see setStopConditions for stopping conditions for character block operations."<br>
- self lastCharacterExtentSetX: (font widthOf: (text at: lastIndex)).<br>
self perform: stopCondition] whileFalse.<br>
<br>
^characterIndex == nil<br>
ifTrue: ["characterBlockAtPoint"<br>
^ CharacterBlock new stringIndex: lastIndex text: text<br>
topLeft: characterPoint + (font descentKern @ 0)<br>
+ extent: lastCharacterWidth @ line lineHeight]<br>
- extent: lastCharacterExtent]<br>
ifFalse: ["characterBlockForIndex"<br>
^ CharacterBlock new stringIndex: lastIndex text: text<br>
topLeft: characterPoint + ((font descentKern) - kern @ 0)<br>
+ extent: lastCharacterWidth @ line lineHeight]!<br>
- extent: lastCharacterExtent]!<br>
<br>
Item was changed:<br>
----- Method: CharacterBlockScanner>>characterBlockAtPoint:index:in: (in category 'scanning') -----<br>
characterBlockAtPoint: aPoint index: index in: textLine<br>
"This method is the Morphic characterBlock finder. It combines<br>
MVC's characterBlockAtPoint:, -ForIndex:, and buildCharacterBlockIn:"<br>
| runLength lineStop stopCondition |<br>
line := textLine.<br>
rightMargin := line rightMargin.<br>
lastIndex := line first.<br>
self setStopConditions. "also sets font"<br>
characterIndex := index. " == nil means scanning for point"<br>
characterPoint := aPoint.<br>
(characterPoint isNil or: [characterPoint y > line bottom])<br>
ifTrue: [characterPoint := line bottomRight].<br>
destX := leftMargin := line leftMarginForAlignment: alignment.<br>
destY := line top.<br>
(text isEmpty or: [(characterPoint y < destY or: [characterPoint x < destX])<br>
or: [characterIndex notNil and: [characterIndex < line first]]])<br>
ifTrue: [^ (CharacterBlock new stringIndex: line first text: text<br>
topLeft: destX@destY extent: 0 @ textStyle lineGrid)<br>
textLine: line].<br>
runLength := text runLengthFor: line first.<br>
lineStop := characterIndex "scanning for index"<br>
ifNil: [ line last ]. "scanning for point"<br>
runStopIndex := lastIndex + (runLength - 1) min: lineStop.<br>
+ lastCharacterWidth := 0.<br>
- lastCharacterExtent := 0 @ line lineHeight.<br>
spaceCount := 0.<br>
<br>
[<br>
stopCondition := self scanCharactersFrom: lastIndex to: runStopIndex<br>
in: text string rightX: characterPoint x<br>
stopConditions: stopConditions kern: kern.<br>
"see setStopConditions for stopping conditions for character block operations."<br>
- self lastCharacterExtentSetX: (specialWidth ifNil: [font widthOf: (text at: lastIndex)]).<br>
self perform: stopCondition<br>
] whileFalse.<br>
characterIndex<br>
+ ifNil: ["Result for characterBlockAtPoint: "<br>
- ifNil: [<br>
- "Result for characterBlockAtPoint: "<br>
- (stopCondition ~~ #cr and: [ lastIndex = line last<br>
- and: [ aPoint x > ((characterPoint x) + (lastCharacterExtent x / 2)) ]])<br>
- ifTrue: [ "Correct for right half of last character in line"<br>
- ^ (CharacterBlock new stringIndex: lastIndex + 1<br>
- text: text<br>
- topLeft: characterPoint + (lastCharacterExtent x @ 0) + (font descentKern @ 0)<br>
- extent: 0 @ lastCharacterExtent y)<br>
- textLine: line ].<br>
^ (CharacterBlock new<br>
stringIndex: lastIndex<br>
text: text topLeft: characterPoint + (font descentKern @ 0)<br>
+ extent: lastCharacterWidth @ line lineHeight - (font baseKern @ 0))<br>
- extent: lastCharacterExtent - (font baseKern @ 0))<br>
textLine: line]<br>
ifNotNil: ["Result for characterBlockForIndex: "<br>
^ (CharacterBlock new<br>
stringIndex: characterIndex<br>
text: text topLeft: characterPoint + ((font descentKern) - kern @ 0)<br>
+ extent: lastCharacterWidth @ line lineHeight)<br>
- extent: lastCharacterExtent)<br>
textLine: line]!<br>
<br>
Item was changed:<br>
----- Method: CharacterBlockScanner>>cr (in category 'stop conditions') -----<br>
cr<br>
"Answer a CharacterBlock that specifies the current location of the mouse<br>
relative to a carriage return stop condition that has just been<br>
encountered. The ParagraphEditor convention is to denote selections by<br>
CharacterBlocks, sometimes including the carriage return (cursor is at<br>
the end) and sometimes not (cursor is in the middle of the text)."<br>
<br>
((characterIndex ~= nil<br>
and: [characterIndex > text size])<br>
or: [(line last = text size)<br>
and: [(destY + line lineHeight) < characterPoint y]])<br>
ifTrue: ["When off end of string, give data for next character"<br>
destY := destY + line lineHeight.<br>
characterPoint := (nextLeftMargin ifNil: [leftMargin]) @ destY.<br>
(lastIndex < text size and: [(text at: lastIndex) = CR and: [(text at: lastIndex+1) = Character lf]])<br>
ifTrue: [lastIndex := lastIndex + 2]<br>
ifFalse: [lastIndex := lastIndex + 1].<br>
+ lastCharacterWidth := 0.<br>
- self lastCharacterExtentSetX: 0.<br>
^ true].<br>
characterPoint := destX @ destY.<br>
+ lastCharacterWidth := rightMargin - destX.<br>
- self lastCharacterExtentSetX: rightMargin - destX.<br>
^true!<br>
<br>
Item was changed:<br>
----- Method: CharacterBlockScanner>>crossedX (in category 'stop conditions') -----<br>
crossedX<br>
"Text display has wrapping. The scanner just found a character past the x<br>
location of the cursor. We know that the cursor is pointing at a character<br>
or before one."<br>
<br>
+ self retrieveLastCharacterWidth.<br>
+<br>
- | currentX lastCharacter |<br>
characterIndex == nil ifFalse: [<br>
"If the last character of the last line is a space,<br>
and it crosses the right margin, then locating<br>
the character block after it is impossible without this hack."<br>
characterIndex > text size ifTrue: [<br>
lastIndex := characterIndex.<br>
characterPoint := (nextLeftMargin ifNil: [leftMargin]) @ (destY + line lineHeight).<br>
^true]].<br>
+ characterPoint x <= (destX + (lastCharacterWidth // 2))<br>
- characterPoint x <= (destX + (lastCharacterExtent x // 2))<br>
ifTrue: [characterPoint := destX @ destY.<br>
^true].<br>
lastIndex >= line last<br>
ifTrue: [characterPoint := destX @ destY.<br>
^true].<br>
<br>
"Pointing past middle of a character, return the next character."<br>
lastIndex := lastIndex + 1.<br>
+ characterPoint := destX + lastCharacterWidth + kern @ destY.<br>
- lastCharacter := text at: lastIndex.<br>
- currentX := destX + lastCharacterExtent x + kern.<br>
- self lastCharacterExtentSetX: (font widthOf: lastCharacter).<br>
- characterPoint := currentX @ destY.<br>
- lastCharacter = Space ifFalse: [^ true].<br>
-<br>
- "Yukky if next character is space or tab."<br>
- alignment = Justified ifTrue:<br>
- [self lastCharacterExtentSetX:<br>
- (lastCharacterExtent x + (line justifiedPadFor: (spaceCount + 1) font: font))].<br>
-<br>
^ true!<br>
<br>
Item was changed:<br>
----- Method: CharacterBlockScanner>>endOfRun (in category 'stop conditions') -----<br>
endOfRun<br>
"Before arriving at the cursor location, the selection has encountered an<br>
end of run. Answer false if the selection continues, true otherwise. Set<br>
up indexes for building the appropriate CharacterBlock."<br>
<br>
+ | runLength lineStop |<br>
+<br>
- | runLength lineStop lastCharacter |<br>
(((characterIndex ~~ nil and:<br>
[runStopIndex < characterIndex and: [runStopIndex < text size]])<br>
or: [characterIndex == nil and: [lastIndex < line last]]) or: [<br>
((lastIndex < line last)<br>
and: [((text at: lastIndex) leadingChar ~= (text at: lastIndex+1) leadingChar)<br>
and: [lastIndex ~= characterIndex]])])<br>
ifTrue: ["We're really at the end of a real run."<br>
+ runLength := text runLengthFor: (lastIndex := lastIndex + 1).<br>
+ lineStop := characterIndex "scanning for index"<br>
+ ifNil: [line last]. "scanning for point".<br>
- runLength := (text runLengthFor: (lastIndex := lastIndex + 1)).<br>
- characterIndex ~~ nil<br>
- ifTrue: [lineStop := characterIndex "scanning for index"]<br>
- ifFalse: [lineStop := line last "scanning for point"].<br>
(runStopIndex := lastIndex + (runLength - 1)) > lineStop<br>
+ ifTrue: [runStopIndex := lineStop].<br>
- ifTrue: [runStopIndex := lineStop].<br>
self setStopConditions.<br>
^false].<br>
<br>
+ self retrieveLastCharacterWidth.<br>
+<br>
+ (characterIndex == nil and: [lastIndex = line last])<br>
+ ifTrue: [characterPoint x > (destX + (lastCharacterWidth // 2))<br>
+ ifTrue:<br>
+ [ "Correct for clicking right half of last character in line<br>
+ means selecting AFTER the char"<br>
+ lastIndex := lastIndex + 1.<br>
+ lastCharacterWidth := 0.<br>
+ characterPoint := destX + lastCharacterWidth @ destY.<br>
+ ^true]].<br>
+<br>
- lastCharacter := text at: lastIndex.<br>
characterPoint := destX @ destY.<br>
- ((lastCharacter = Space and: [alignment = Justified])<br>
- or: [lastCharacter = Tab and: [lastSpaceOrTabExtent notNil]])<br>
- ifTrue: [lastCharacterExtent := lastSpaceOrTabExtent].<br>
characterIndex ~~ nil<br>
ifTrue: ["If scanning for an index and we've stopped on that index,<br>
then we back destX off by the width of the character stopped on<br>
(it will be pointing at the right side of the character) and return"<br>
runStopIndex = characterIndex<br>
+ ifTrue: [characterPoint := destX - lastCharacterWidth @ destY.<br>
- ifTrue: [self characterPointSetX: destX - lastCharacterExtent x.<br>
^true].<br>
"Otherwise the requested index was greater than the length of the<br>
string. Return string size + 1 as index, indicate further that off the<br>
string by setting character to nil and the extent to 0."<br>
lastIndex := lastIndex + 1.<br>
+ lastCharacterWidth := 0.<br>
- self lastCharacterExtentSetX: 0.<br>
^true].<br>
<br>
"Scanning for a point and either off the end of the line or off the end of the string."<br>
runStopIndex = text size<br>
ifTrue: ["off end of string"<br>
lastIndex := lastIndex + 1.<br>
+ lastCharacterWidth := 0.<br>
- self lastCharacterExtentSetX: 0.<br>
^true].<br>
"just off end of line without crossing x"<br>
lastIndex := lastIndex + 1.<br>
^true!<br>
<br>
Item was removed:<br>
- ----- Method: CharacterBlockScanner>>lastCharacterExtentSetX: (in category 'private') -----<br>
- lastCharacterExtentSetX: xVal<br>
- lastCharacterExtent := xVal @ lastCharacterExtent y!<br>
<br>
Item was removed:<br>
- ----- Method: CharacterBlockScanner>>lastSpaceOrTabExtentSetX: (in category 'private') -----<br>
- lastSpaceOrTabExtentSetX: xVal<br>
- lastSpaceOrTabExtent := xVal @ lastSpaceOrTabExtent y!<br>
<br>
Item was changed:<br>
----- Method: CharacterBlockScanner>>paddedSpace (in category 'stop conditions') -----<br>
paddedSpace<br>
"When the line is justified, the spaces will not be the same as the font's<br>
space character. A padding of extra space must be considered in trying<br>
to find which character the cursor is pointing at. Answer whether the<br>
scanning has crossed the cursor."<br>
<br>
| pad |<br>
- pad := 0.<br>
spaceCount := spaceCount + 1.<br>
pad := line justifiedPadFor: spaceCount font: font.<br>
+ lastCharacterWidth := spaceWidth + pad.<br>
+ (destX + lastCharacterWidth) >= characterPoint x<br>
+ ifTrue:<br>
+ [^self crossedX].<br>
- lastSpaceOrTabExtent := lastCharacterExtent.<br>
- self lastSpaceOrTabExtentSetX: spaceWidth + pad.<br>
- (destX + lastSpaceOrTabExtent x) >= characterPoint x<br>
- ifTrue: [lastCharacterExtent := lastSpaceOrTabExtent.<br>
- ^self crossedX].<br>
lastIndex := lastIndex + 1.<br>
+ destX := destX + lastCharacterWidth.<br>
- destX := destX + lastSpaceOrTabExtent x.<br>
^ false<br>
!<br>
<br>
Item was added:<br>
+ ----- Method: CharacterBlockScanner>>retrieveLastCharacterWidth (in category 'private') -----<br>
+ retrieveLastCharacterWidth<br>
+ | lastCharacter |<br>
+ lastIndex > text size ifTrue: [^lastCharacterWidth := 0].<br>
+ lastCharacter := text at: lastIndex.<br>
+ (lastCharacter charCode >= 256 or: [(stopConditions at: lastCharacter charCode + 1) isNil])<br>
+ ifTrue: [lastCharacterWidth := font widthOf: (text at: lastIndex)].<br>
+ "if last character was a stop condition, then the width is already set"<br>
+ ^lastCharacterWidth!<br>
<br>
Item was changed:<br>
----- Method: CharacterBlockScanner>>space (in category 'stop conditions') -----<br>
space<br>
"Account for spaceWidth"<br>
<br>
spaceCount := spaceCount + 1.<br>
+ lastCharacterWidth := spaceWidth.<br>
+ (destX + lastCharacterWidth) >= characterPoint x<br>
+ ifTrue:<br>
+ [^self crossedX].<br>
- lastSpaceOrTabExtent := lastCharacterExtent.<br>
- self lastSpaceOrTabExtentSetX: spaceWidth.<br>
- (destX + spaceWidth) >= characterPoint x<br>
- ifTrue: [lastCharacterExtent := lastSpaceOrTabExtent.<br>
- ^self crossedX].<br>
lastIndex := lastIndex + 1.<br>
+ destX := destX + lastCharacterWidth.<br>
+ ^ false!<br>
- destX := destX + spaceWidth.<br>
- ^ false<br>
- !<br>
<br>
Item was changed:<br>
----- Method: CharacterBlockScanner>>tab (in category 'stop conditions') -----<br>
tab<br>
| currentX |<br>
currentX := (alignment = Justified and: [self leadingTab not])<br>
ifTrue: "imbedded tabs in justified text are weird"<br>
[destX + (textStyle tabWidth - (line justifiedTabDeltaFor: spaceCount)) max: destX]<br>
ifFalse:<br>
[textStyle<br>
nextTabXFrom: destX<br>
leftMargin: leftMargin<br>
rightMargin: rightMargin].<br>
+ lastCharacterWidth := currentX - destX max: 0.<br>
- lastSpaceOrTabExtent := lastCharacterExtent.<br>
- self lastSpaceOrTabExtentSetX: (currentX - destX max: 0).<br>
currentX >= characterPoint x<br>
ifTrue:<br>
+ [^ self crossedX].<br>
- [lastCharacterExtent := lastSpaceOrTabExtent.<br>
- ^ self crossedX].<br>
destX := currentX.<br>
lastIndex := lastIndex + 1.<br>
^false!<br>
<br>
<br>
</blockquote></div><br></div>