ホームページ>開発ツール>Xojo / Real Studio Trial and Error・CocoaのDeclareでリッチテキストを扱う・縦書き時の不具合について
Xojo / Real Studio Trial and Error
目次
CocoaのDeclareでリッチテキストを扱う・縦書き時の不具合について
はじめに
以下は、Xojo Cocoa 64bitビルドについての話題です。
縦書き時の不具合について調べてみました。
なお検証には、Xojo 2017 Release 1.1を用いています。(Mac mini 2018 + macOS 10.14.6 Mojave)
経緯
macOS 10.14 Mojave以降では、縦書き時に、行位置がズレる現象が発生します。(注:OS X 10.13 High Sierra以前では問題ない。)
この現象は本プロジェクトのみでなく、アップル純正の「テキストエディット」でも生じるので、システム側の問題のようだ。挙動を観察してみると、表示されるのはTextAreaの高さに相当する位置のようでした。
実際、ウィンドウの高さを変化させると、それに合わせて行位置が左右に移動します。
ということは、領域(Frame)の幅に高さの値が割り当てられている可能性が考えられるので、TextAreaの幅と高さがどうなっているのか、調べてみることにしました。
XojoのTextAreaは(多分)CocoaのNSScrollViewを継承していて、その(文字入力に関連する)サブビューの構成は以下のようになっています。
NSScrollView | NSTextView | NSClipView
それぞれの幅と高さを調べたところ、TextViewのみ、縦書き時は、幅に高さの値がセットされていました。
TextViewとClipViewのFrameは本来一致する筈のものなので、然るべきタイミングでTextViewのFrameを再設定すれば、解消することができそうです。
タイミングとして考えられるものは、
1. 文字入力
2. クリップボードからのペースト
3. ドラッグ&ドロップ
4. リサイズ(今回はウィンドウのリサイズ時が該当)
5. 既に文字が入っている時の縦書き指定
1.については、当初XojoのKeyDown,KeyUpイベント辺りでなんとかなるかと思ったのですが、値の変更が起きるのはKeyDown後からKeyUp迄の間のようで、KeyUpで書き換えると、KeyDownで移動してKeyUpで元に戻るを繰り返す、という、ちょっと煩わしい状態に陥ってしまいます。
結局、NSTextViewのdidChangeTextでなら対処できることを確認しました。
2.についてはNSTextViewのpaste:、3.については同じくconcludeDragOperation:で対処できることを確認しました。
didChangeTextとconcludeDragOperation:は通常のDelegateでもいいのですが、既にMethod Swizzlingが実装済なので、同じやり方で追加することにします。
4.についてはウィンドウのResizingイベントで対処できることを確認しました。
5.については、NSAttributedStringに対する縦書き属性の設定をしないことで、対処できることを確認しました。
この設定は、編集ではなくプレビュー時の不具合対策用でしたが、前回のプレビュー方式の変更で必要なくなっていましたので、削っても問題ありません。
以上を踏まえ、(残りの)仕様は以下の通りとしました。
- 再設定はクラス側(共有メソッド)で行い、インスタンス側への移譲は行わない。(必要ならカスタマイズして下さい。)
- 今回の処理は横書き時は不要のものだが、判定処理は行わない。(必要ならカスタマイズして下さい。)
Xojoでの実装
【ソースコードのコピー&ペーストについて】
・ソースコード(グレー背景部分の全文)をコピーし、指定のオブジェクトにペーストすると、(新規作成して名前等を個別にコピー&ペーストしなくても)復元されます。
・ペーストはオブジェクトに行って下さい。オブジェクト内のEvent Handlers/Methods/Properties等にペーストしても、うまくいかない場合があります。
・それでもペーストできない場合は、各項目のカッコ内を適用して下さい。
実行してみたところ、縦書き時に行位置がズレる現象が解消されることを確認しました。
- 前回プロジェクトをベースとする(64bitビルド)
- 以下をWindow1にペースト
Sub Resizing() Handles Resizing // 縦書き時の横方向行位置補正 ResetTextViewFrame() End Sub
- 以下をWindow1のOpenイベント内に追加(me.Height=me.Heightの直後)
// TextArea1のTextViewとClipViewを取得して保持 GetViewPtr()
- 以下をWindow1にペースト
Private Sub GetViewPtr() Dim i, cnt As Integer Dim pnt3, pnt4 As Ptr Dim st As String // NSTextViewの取得 declare function documentView lib "Cocoa" selector "documentView" (obj_id as Integer) as Ptr // Return NSTextView* Dim pnt1 As Ptr = documentView(TextArea1.Handle) // TextArea1 = NSScrollView // サブビュー配列を取得 Declare Function subviews Lib "Cocoa" selector "subviews" (class_id As Integer) As Ptr Dim pnt2 As Ptr = subviews(TextArea1.Handle) // サブビューの個数を取得 Declare Function count Lib "Cocoa" selector "count" (class_id As Ptr) As Integer cnt = count(pnt2) Declare Function objectAtIndex Lib "Cocoa" selector "objectAtIndex:" (class_id As Ptr, idx As Integer) As Ptr Declare Function myClass Lib "Cocoa" selector "class" (class_id As Ptr) As Ptr Declare Function description Lib "Cocoa" selector "description" (class_id As Ptr) As CFStringRef // サブビュー数だけ回す for i=0 to cnt-1 // サブビューを順に取得 pnt3 = objectAtIndex(pnt2, i) // サブビューのクラスを取得 pnt4 = myClass(pnt3) // クラス名を取得 st = description(pnt4) // NSClipViewなら if st="NSClipView" then // TextViewとClipViewを共有プロパティに保持 textVIew=pnt1 clipView=pnt3 exit end if next End Sub
- Window1のToggleVtextを削除後、以下をペースト
Private Sub ToggleVtext() // トグル動作 Dim flg As Integer if vFlg=false then vFlg=true flg=1 else vFlg=false flg=0 end if // NSTextViewの取得 declare function documentView lib "Cocoa" selector "documentView" (obj_id as Integer) as Ptr // Return NSTextView* Dim pnt1 As Ptr = documentView(TextArea1.Handle) // NSTextViewに対する縦書き属性の設定(NSTextLayoutOrientationVertical = 1) declare sub setLayoutOrientation lib "Cocoa" selector "setLayoutOrientation:" (receiver as Ptr, flag as Integer) setLayoutOrientation(pnt1, flg) End Sub
- 以下をWindow1にペースト
Protected Shared Sub myConcludeDragOperation(id as Ptr, sel as Ptr, sender As Ptr) // 本来のconcludeDragOperation:を実行 declare sub myConcludeDragOperation lib "Cocoa" selector "myConcludeDragOperation:" (receiver as Ptr, sender As Ptr) myConcludeDragOperation(id, sender) // 縦書き時の横方向行位置補正 ResetTextViewFrame() End Sub
- 以下をWindow1にペースト
Protected Shared Sub myDidChangeText(id as Ptr, sel as Ptr) // 本来のdidChangeTextを実行 declare sub myDidChangeText lib "Cocoa" selector "myDidChangeText" (receiver as Ptr) myDidChangeText(id) // 縦書き時の横方向行位置補正 ResetTextViewFrame() End Sub
- Window1のmyPasteの最後に以下を追加
// 縦書き時の横方向行位置補正 ResetTextViewFrame()
- Window1のRegisterMethodNSTextViewの最後に以下を追加
// NSTextViewクラスにmyDidChangeTextメソッドを追加(Xojo側で用意したmyDidChangeTextメソッドで受け取る。) if not class_addMethod (TextViewPtr, NSSelectorFromString("myDidChangeText"), AddressOf myDidChangeText, "v24@0:8@16") then msgBox "error12." return end if // NSTextViewクラスにmyConcludeDragOperation:メソッドを追加(Xojo側で用意したmyConcludeDragOperationメソッドで受け取る。) if not class_addMethod (TextViewPtr, NSSelectorFromString("myConcludeDragOperation:"), AddressOf myConcludeDragOperation, "c24@0:8@16") then msgBox "error13." return end if
- Window1のsetupHookの最後に以下を追加
orgMethod = class_getInstanceMethod(TextViewPtr, NSSelectorFromString("didChangeText")) myMethod = class_getInstanceMethod(TextViewPtr, NSSelectorFromString("myDidChangeText")) method_exchangeImplementations(orgMethod, myMethod) orgMethod = class_getInstanceMethod(TextViewPtr, NSSelectorFromString("concludeDragOperation:")) myMethod = class_getInstanceMethod(TextViewPtr, NSSelectorFromString("myConcludeDragOperation:")) method_exchangeImplementations(orgMethod, myMethod)
- 以下をWindow1にペースト
Private Shared Sub ResetTextViewFrame() // ClipViewのFrameを取得 Declare Function frame Lib "Cocoa" selector "frame" (class_id As Ptr) As NSRect Dim rect As NSRect = frame(clipView) // ClipViewのFrameをTextViewに適用する Declare Sub setFrame Lib "Cocoa" selector "setFrame:" (class_id As Ptr, rect As NSRect) setFrame(textView, rect) End Sub
- 以下をWindow1にペースト(できなければ共有プロパティに、名前:clipView、データ型:Ptr、を追加)
Private Shared Property clipView as Ptr
- 以下をWindow1にペースト(できなければ共有プロパティに、名前:textVIew、データ型:Ptr、を追加)
Private Shared Property textVIew as Ptr
おわりに
対症療法的に対応しているので、まだ漏れがあるかもしれません。今後も何かあれば追加していきたいと思っています。
お世話になったサイト
貴重な情報をご提供頂いている皆様に、お礼申し上げます。(以下、順不同)
更新履歴
2020.07.13 新規作成
[Home] [MacSoft] [Donation] [History] [Privacy Policy] [Affiliate Policy]