ホームページ開発ツール>Xojo / Real Studio Trial and Error・CocoaのDeclareでリッチテキストを扱う・縦書きプレビュー改

 Xojo / Real Studio Trial and Error

CocoaのDeclareでリッチテキストを扱う・縦書きプレビュー改

目次
 はじめに

 以下は、Xojo Cocoaビルドについての話題です。

 プレビュー機能の問題点と対策について試してみました。
 なお検証には、Xojo 2016 Release 1.1を用いています。(Mac mini mid 2010 + OS X 10.13.6 High Sierra)


 プレビュー機能の問題点と対策

 プレビュー機能について、前回の方法では、(1)縦書き時、(2)画像を含む場合、以下の問題点が確認されました。
  1. 画像が回転してしまう。
  2. テキストのベースライン位置がズレる。
S Shot1 S Shot1
ウィンドウ上での表示 プレビュー(前回版)

 1.について、当初は、画像のみをあらかじめ逆方向に回転しておき、相殺させることを考えたのですが、画像はファイルの状態で格納されていることから、直接ビットマップデータにアクセスするのは難し(ややこし?)そうだったので、断念しました。

 次に考えたのは、テキストエリアのキャプチャーができないか、ということでした。
 キャプチャーであれば、1.だけでなく、2.も同時に解決できます。
 調べところ、テキストエリアは見つけられませんでしたが、ウィンドウのキャプチャーが見つかりました。

 参考サイト(1):(旧) Cocoaの日々: スクリーンショットを撮る
注)上記サイトではCGWindowIDの代わりにNSWindow#windowNumberを使うことの懸念が示されているが、本試行においては特に支障はなさそうだったので、そのまま使わせて頂いた。
 ウィンドウ用なので、範囲を指定しないと、ウィンドウやスクリーン全体をキャプチャーしてしまうのですが、範囲を指定すれば、所望の部分だけ抜き出せることが分かりましたので、これを使うことにしました。
 なお、範囲指定は、例によって、現物合わせとしています。


 Xojoでの実装
【ソースコードのコピー&ペーストについて】
ソースコード(グレー背景部分の全文)をコピーし、指定のウィンドウ/クラスにペーストすると、(新規作成して名前等を個別にコピー&ペーストしなくても)復元されます。
ただし、この方法は、メソッドでは問題ないようですが、イベント/アクション/プロパティでは不安定?なので、ペーストできない場合は、各項目のカッコ内を適用して下さい。
  1. 前回プロジェクトをベースとする
  2. Window1内のPNGfromAttrStringメソッドを以下に差し替え
    Private Function PNGfromAttrString() as Ptr
      // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // TextAreaの取得
      declare function documentView lib "Cocoa" selector "documentView" (obj_id as Integer) as Ptr  // Return NSTextView*
      Dim pnt1 As Ptr = documentView(TextArea1.Handle)  // ef.Handle = NSScrollView*
      
      // NSTextStorageの取得>NSAttributedStringの取得
      declare function textStorage lib "Cocoa" selector "textStorage" (obj_id as Ptr) As Ptr  // Return NSTextStorage*
      Dim pnt2 As Ptr = textStorage(pnt1)
      
      // サイズのセット (スクロールバー、ルーラーの領域分をそれぞれ引いている。数値は現物合わせのため、誤差を含む可能性あり。)
      Dim rect As CGRect
      if vFlg=false then  // 横
        rect = CGRectMake(me.Left+TextArea1.Left,me.Top+TextArea1.Top+kTopMgnH,TextArea1.Width-kRightMgnH,TextArea1.Height-kBottomMgnH)
      else  // 縦
        rect = CGRectMake(me.Left+TextArea1.Left+kLeftMgnV,me.Top+TextArea1.Top,TextArea1.Width-kRightMgnV,TextArea1.Height-kBottomMgnV)
      end if
      
      // ウィンドウナンバーの取得
      Declare Function windowNumber Lib "Cocoa" Selector "windowNumber" (receiver As Integer) As Integer
      Dim window_id As Integer = windowNumber(me.Handle)
      
      // ウィンドウのキャプチャー(Bitwise.ShiftLeft(1,3) = kCGWindowListOptionIncludingWindow (1 << 3)、0 = kCGWindowImageDefault)
      Declare Function CGWindowListCreateImage Lib "Carbon" (rect As CGRect, winopt As Integer, winid As Integer, imgopt As Integer) As Ptr
      Dim cgimage As Ptr = CGWindowListCreateImage(rect, Bitwise.ShiftLeft(1,3), window_id, 0)
      
      // NSBitmapImageRep初期化
      Dim bitmapImageRep As Ptr = NSClassFromString("NSBitmapImageRep")
      Declare Function alloc Lib "Cocoa" Selector "alloc" (receiver As Ptr) As Ptr
      bitmapImageRep = alloc(bitmapImageRep)
      Declare Function initWithCGImage Lib "Cocoa" Selector "initWithCGImage:" (receiver As Ptr, img As Ptr) As Ptr
      bitmapImageRep = initWithCGImage(bitmapImageRep, cgimage)
      
      // falseをNSNumber形式に変換
      Dim numb As Ptr = NSClassFromString("NSNumber")
      Declare Function numberWithBool Lib "Cocoa" Selector "numberWithBool:" (receiver As Ptr, path As Boolean) As Ptr
      numb = numberWithBool(numb, false)
      
      // PNG用オプションのセット
      Dim dict As Ptr = NSClassFromString("NSDictionary")
      Declare Function dictionaryWithObject Lib "Cocoa" Selector "dictionaryWithObject:forKey:" (receiver As Ptr, objt As Ptr, key As CFStringRef) As Ptr
      dict = dictionaryWithObject(dict, numb, "NSImageInterlaced")
      
      // PNG出力(NSPNGFileType = 4)
      Declare Function representationUsingType Lib "Cocoa" Selector "representationUsingType:properties:" (receiver As Ptr, type As Integer, prop As Ptr) As Ptr
      Dim pngData As Ptr = representationUsingType(bitmapImageRep, 4, dict)
      
      // clean up
      Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr)
      release(bitmapImageRep)
      
      // PNG形式で返す
      return pngData
    End Function
    
  3. Window1内のPNGfromAttrString2メソッドを削除
  4. 以下をModule1にペースト(できなければ定数に、名前:kBottomMgnH、デフォルト値:33、データ型:Number、を追加)
    Private Const kBottomMgnH as Number = 33
    
  5. 以下をModule1にペースト(できなければ定数に、名前:kBottomMgnH、デフォルト値:16、データ型:Number、を追加)
    Private Const kBottomMgnV as Number = 16
    
  6. 以下をModule1にペースト(できなければ定数に、名前:kLeftMgnV、デフォルト値:29、データ型:Number、を追加)
    Private Const kLeftMgnV as Number = 29
    
  7. 以下をModule1にペースト(できなければ定数に、名前:kRightMgnH、デフォルト値:16、データ型:Number、を追加)
    Private Const kRightMgnH as Number = 16
    
  8. 以下をModule1にペースト(できなければ定数に、名前:kRightMgnV、デフォルト値:30、データ型:Number、を追加)
    Private Const kRightMgnV as Number = 30
    
  9. 以下をModule1にペースト(できなければ定数に、名前:kTopMgnH、デフォルト値:31、データ型:Number、を追加)
    Private Const kTopMgnH as Number = 31
    
 実行してみたところ、テキストエリアのままのプレビューが得られることを確認しました。
S Shot1 S Shot1


 おわりに

 キャプチャーなので、キャレットが表示されている時にプレビューすると、キャレットも表示されてしまいます。
 キャレットを表示させたくない(あるいは、させたい)場合は、ブリンクのタイミングを見計らう必要があります。

 また、繰り返しになりますが、範囲指定は現物合わせなので、状況によっては位置/サイズの再調整が必要になるかもしれません。
 コントロールレベルでキャプチャーする方法を模索する等した方がいいかもしれません。

 あと、ウィンドウのキャプチャーは面白いです。これはこれで、あれこれ試してみるのもありかも。


 お世話になったサイト

 貴重な情報をご提供頂いている皆様に、お礼申し上げます。(以下、順不同)

 参考サイト(1):(旧) Cocoaの日々: スクリーンショットを撮る


 更新履歴

 2019.09.06 新規作成


[Home]  [MacSoft]  [Donation]  [History]  [Privacy Policy]  [Affiliate Policy]