IMMウィンドウ制御
少し本腰を入れて調べています。
Text関係Widgetの内部構造
Player→Costumeの入れ子をたぐっていくと、一番内側では CCostumeTextHolderというアスペクトが、実際のデータ構造を管理するCTextParagraphEditor オブジェクトを保持しています。データ構造に対するいろんなユーザ操作がありますが、最終的には#selectFrom:To:, #zapSelectionWith:あたりに内部処理が集約されるようです。このあたりをトリガとして、変換ウィンドウ位置の設定処理を呼び出すようにしてみます。
CompositionWindowManagerへの要求を出すのは誰か?
この場合、以下の2種類が考えられました:
今回の場合、クラス一箇所で実装すれば済む前者を採りました。
Tweakは委譲をフルに活用して、きれいに責務を割り当てています。上記の設計がTweakのフレームワークの考え方に合っているのかどうか、今回もっとも自信がないところです。
変換ウィンドウの位置
CTextParagraphEditor>>selectionRect というのがあるので、とりあえずその左上を使ってみます。この結果は、コスチュームのローカル座標系なので、まず Tweak世界のグローバル座標系に変換します。
Tweak→Morphicの座標変換は、今回は単純にCWorldPlayerの親のCWorldMorphの座標を足しました。今後、Morphicを取り除いても影響が出ないよう処理を隔離する必要があるでしょう。
要検討事項の残
- ウィンドウ位置の正確な計算。今は少しだけずれています。
- Tweak世界中でのコスチューム移動の把握(親ごと移動した、スクロールされたなど)
- Morphic世界中での CRootMorph/CWorldPlayer移動の把握
- マウスクリック以外の方法でのフォーカス移動の把握
- MorphicのウィンドウからTweakの最上位ウィンドウにフォーカスを切り替えたことの把握
プロトタイプ(tweak-ja 5/27版へのチェンジセット)
日記にチェンジセットを貼るのもどうよ?って感じですが、、今ここしか発表場所がないのですみません ^^;) CompositionWindowManagerの管理など各所で手抜きをしていています。またWin32でしか動きません。これは今後見直します。
'From Squeak3.8 of ''5 May 2005'' [latest update: #6665] on 2 June 2005 at 0:56:32 pm'! CCostumeAspect subclass: #CCostumeTextHolder instanceVariableNames: '<?xml version="1.0" ?> <fields> <field instanceVariable="true" toSet="text:">text</field> <field instanceVariable="true">paragraph</field> <field instanceVariable="true">editor</field> <field instanceVariable="true" toSet="offset:">offset</field> <field instanceVariable="true" toSet="color:">color</field> <field instanceVariable="true" toSet="borderWidth:">borderWidth</field> <field instanceVariable="true" toSet="borderColor:">borderColor</field> <field instanceVariable="true" toSet="anchorPoint:">anchorPoint</field> <field instanceVariable="true" toSet="inset:">inset</field> <field instanceVariable="true">displaying</field> <field instanceVariable="true" toSet="showCaret:">showCaret</field> <field instanceVariable="true" toSet="wrapContents:">wrapContents</field> <field instanceVariable="true" toSet="emphasis:">emphasis</field> <field instanceVariable="true" toSet="font:">font</field> </fields>' classVariableNames: 'CompositionWindowManager DefaultBorder' poolDictionaries: '' category: 'Tweak-Costume-Aspects'! !CCostumeTextHolder methodsFor: 'accessing' stamp: 'KR 6/2/2005 12:15'! inputPointMoved self setCompositionWindow! ! !CCostumeTextHolder methodsFor: 'private' stamp: 'KR 6/2/2005 12:31'! compositionWindowManager CompositionWindowManager ifNotNil: [^CompositionWindowManager]. ^CompositionWindowManager := ImmWin32 new. ! ! !CCostumeTextHolder methodsFor: 'private' stamp: 'KR 6/2/2005 12:55'! setCompositionWindow | inputPointLocal inputPointWorld x y| inputPointLocal := costume localToGlobal: (self editor selectionRect topLeft). inputPointWorld := inputPointLocal + (costume root ownerMorph topLeft). x := inputPointWorld x. y := inputPointWorld y. self compositionWindowManager setCompositionWindowPositionX: x y: y.! ! !CTextParagraphEditor methodsFor: 'private' stamp: 'KR 6/2/2005 12:16'! inputPointMoved costume inputPointMoved.! ! !CTextParagraphEditor methodsFor: 'private' stamp: 'KR 6/2/2005 12:05'! zapSelectionWith: aText | start stop | (aText isEmpty and:[markBlock = pointBlock]) ifTrue:[^self]. start := self selectionStart. stop := self selectionStop-1. aText isString ifTrue:[ self replaceFrom: start to: stop with: (Text string: aText emphasis: self emphasisHere). ] ifFalse:[ self replaceFrom: start to: stop with: aText. ]. "Need to recompute entirely since paragraph changes" markBlock := paragraph characterBlockAtIndex: start + aText size. pointBlock := markBlock copy. costume invalidate. costume scrollToSelection. self modified: true. self inputPointMoved.! ! !CTextParagraphEditor methodsFor: 'selecting' stamp: 'KR 6/2/2005 12:52'! selectFrom: markIndex to: pointIndex "Select the text from markIndex to pointIndex." | oldRect newRect start stop | lastUndoType := nil. "so we need to reset" oldRect := self selectionRect. start := markIndex min: self text size + 1 max: 1. stop := pointIndex min: self text size + 1 max: 1. "Optimize search for character blocks by reusing the old mark block and testing for start=stop" (start = markBlock stringIndex and:[stop = pointBlock stringIndex]) ifTrue:[^self]. start = markBlock stringIndex ifFalse:[markBlock := paragraph characterBlockAtIndex: start]. start = stop ifTrue:[pointBlock := markBlock copy] ifFalse:[pointBlock := paragraph characterBlockAtIndex: stop]. "Invalidate the regions that have changed" newRect := self selectionRect. costume invalidate: (oldRect merge: newRect). costume scrollToSelection. self updateEmphasisHere. self textSelectionChanged. self inputPointMoved.! !