ホームページ>開発ツール>Xojo / Real Studio Trial and Error・CocoaのDeclareでリッチテキストを扱う
Xojo / Real Studio Trial and Error
目次
CocoaのDeclareでリッチテキストを扱う
はじめに
以下は、Xojo Cocoaビルドについての話題です。
Xojoは標準でリッチテキストに対応していますが、以下の制約があります。
一方、以前にも触れたとおり、XojoのTextAreaはNSTextView(NSScrollView)を継承している(と思われる)のですが、NSTextViewは上記には全て対応しています。なので、使えるか、調べてみました。
- スタイル設定のための標準的な道具立てがなく、ポップアップメニューやキャンバス等を駆使して、自前で実装する必要がある。
- 表、リスト(箇条書き)、リンク、画像等の外部オブジェクトの挿入、に対応していない。
- 表、リスト、リンク、外部オブジェクトを含んだテキストのファイル読み書きに対応していない。(元々テキスト自体が持てない訳だから、非対応なのは当然とも言えるが…)
なお検証には、Xojo 2015 Release 2.4を用いています。(Mac mini mid 2010 + OS X 10.11.1 El Capitan)
方針
リッチテキストの編集については、基本的にNSTextViewに任せる(注1参照)ので、実装はバーやパネルの呼び出し部分について行います。
外部オブジェクトの追加については、今回は全てのフォルダアイテムを対象とします(注2参照)。この部分は自前で実装します。NSTextViewのDelegateを利用する方法もありそうですが、今回はXojoのDropObjectイベントを利用することにします。
ファイルIOに関しては、読み込みはrtf/rtfdの双方、書き出しは(外部オブジェクトを含まなくても)rtfdのみとします。
なお、パネルやファイル読み書きメソッドの呼び出しは、今回はツールバーを利用することとします。(メニューやボタンを利用したい場合は、適宜コードを書き換えて下さい。)
注1)NSTextViewは、直接リッチテキストを扱っている訳ではなく、内部的にはNSAttributedString(NSMutableAttributedString)という形式で持っているようです。
ただ、リッチテキストとの親和性は高いようで、ファイルIOも専用のメソッドが用意されていたりします。
参考サイト(1):添付書類付きリッチテキスト(.rtfd)の実態 - ザリガニが見ていた...。
注2)以下は当方での了解事項です。(単に理解が不足しているだけかもしれませんが…)
XojoのTextAreaは、何もしないと、全てのフォルダアイテムのドロップを受け付けて、そのパスを表示します。この時、DropObjectイベントはレイズされません。
AcceptFileDropを指定してやると、パスは表示されず、DropObjectイベントがレイズされますが、この場合も指定したファイルタイプだけでなく、全てのフォルダアイテムがドロップの対象となります。
ちなみに、Real Studio 2011r3で同じくAcceptFileDropを指定してやると、Carbnビルドでは指定したファイルタイプだけドロップを受け付け、CocoaビルドではXojoと同じ挙動となります。
Xojoでの実装
実装にあたっては、以下のサイトを参考にさせて頂きました。
参考サイト(2):24 | 3月 | 2008 | QueueSoft Developing Diary
- Xojoで新規プロジェクトを作成
- ファイルタイプグループをプロジェクトに追加(Name:FileTypes1)し、一般的なファイルタイプの追加の中から、「application/rtf」を追加。更に以下を追加。
表示名:application/rtfd オブジェクト名:Rtfd Macタイプ:fold Macクリエータ:MACS 拡張子:.rtfd UTI:com.apple.package
- TextAreaをWindow1に追加(Name:TextArea1)
Styledプロパティはオンにしておく。(Defaultでそうなっている筈)- Toolbarをプロジェクトに追加(Name:Toolbar1)後、以下のアイテムを追加。その後、Window1に追加(Name:Toolbar11)。
ToolItem1(開く)/ToolItem2(保存)/ToolItem3(スペース)/ToolItem4(表)/ToolItem5(リンク)- 以下をWindow1のOpenイベントに記述
// NSTextViewの取得 Dim pnt1 As Ptr declare function documentView lib "Cocoa" selector "documentView" (obj_id as Integer) as Ptr // Return NSTextView* pnt1 = documentView(TextArea1.Handle) // インスペクターバーを表示 declare sub setUsesInspectorBar lib "Cocoa" selector "setUsesInspectorBar:" (receiver as Ptr, flag as Boolean) setUsesInspectorBar(pnt1, true) // ルーラーを表示 declare sub setRulerVisible lib "Cocoa" selector "setRulerVisible:" (receiver as Ptr, flag as Boolean) setRulerVisible(pnt1, true) // インスペクターバー付加によるHeightの増加に、ウィンドウが追随しないことへの対策(なぜか、これでうまくいくみたい。)(2017.08.15) me.Height=me.Height
- 以下をWindow1のメソッドに追加
メソッド名: OpenFile Dim dlg As OpenDialog Dim f As FolderItem dlg = New OpenDialog dlg.Filter = FileTypes1.Rtf + FileTypes1.Rtfd f = dlg.ShowModalWithin(self) if f = nil then return end if // パスの取得 Dim filepath As String = f.NativePath // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // TextAreaの取得 Dim pnt1 As Ptr declare function documentView lib "Cocoa" selector "documentView" (obj_id as Integer) as Ptr // Return NSTextView* pnt1 = documentView(TextArea1.Handle) // TextArea1.Handle = NSScrollVew* // RTFの読み込み(rtfもrtfdも、どちらも読める) declare sub readRTFDFromFile lib "Cocoa" selector "readRTFDFromFile:" (obj_id as ptr, path As CFStringRef) readRTFDFromFile(pnt1, filepath)
- 以下をWindow1のメソッドに追加
メソッド名: SaveFile Dim dlg as New SaveAsDialog Dim f as FolderItem dlg.Filter = FileTypes1.Rtfd f=dlg.ShowModalWithin(self) if f = nil then return end if // パスの取得 Dim filepath As String = f.NativePath // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // TextAreaの取得 Dim pnt1 As Ptr declare function documentView lib "Cocoa" selector "documentView" (obj_id as Integer) as Ptr // Return NSTextView* pnt1 = documentView(TextArea1.Handle) // TextArea1.Handle = NSScrollVew* // RTFの書き出し(常にrtfd形式) declare sub writeRTFDToFile lib "Cocoa" selector "writeRTFDToFile:atomically:" (obj_id as ptr, path As CFStringRef, flag As Boolean) writeRTFDToFile(pnt1, filepath, true)
- 以下をTextArea1のOpenイベントに追加
注)当方で確認した限りでは、上記だけで全てのフォルダアイテムがドロップ対象になります。me.AcceptFileDrop("application/rtf")
- 以下をTextArea1のDropObjectイベントに追加
注)上記方法でドロップは実現できますが、一部機能に制限がつくため、NSTextViewネイティブのメソッドを利用した方が良さそうです。詳細はこちらをご覧下さい。(2016.05.01)if Obj.FolderItemAvailable then // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // パスの取得 Dim filepath As String = obj.FolderItem.NativePath // NSTextViewの取得 Dim pnt1 As Ptr declare function documentView lib "Cocoa" selector "documentView" (obj_id as Integer) as Ptr // Return NSTextView* pnt1 = documentView(TextArea1.Handle) // NSTextStorageの取得 Dim pnt2 As Ptr declare function textStorage lib "Cocoa" selector "textStorage" (obj_id as Ptr) As Ptr // Return NSTextStorage* pnt2 = textStorage(pnt1) Declare Function alloc Lib "Cocoa" Selector "alloc" (receiver As Ptr) As Ptr Dim wrap As Ptr = NSClassFromString("NSFileWrapper") wrap = alloc(wrap) Declare Function pathInit Lib "Cocoa" Selector "initWithPath:" (receiver As Ptr, path As CFStringRef) As Ptr wrap = pathInit(wrap, filepath) Dim attachment As Ptr = NSClassFromString("NSTextAttachment") attachment = alloc(attachment) Declare Function wrapInit Lib "Cocoa" Selector "initWithFileWrapper:" (receiver As Ptr, wrapper As Ptr) As Ptr attachment = wrapInit(attachment, wrap) Dim attachChar As Ptr = NSClassFromString("NSAttributedString") Declare Function attributedStringWithAttachment Lib "Cocoa" Selector "attributedStringWithAttachment:" (receiver As Ptr, attachment As Ptr) As Ptr attachChar = attributedStringWithAttachment(attachChar, attachment) Dim attrString As Ptr = NSClassFromString("NSMutableAttributedString") attrString = alloc(attrString) Declare Function initWithString Lib "Cocoa" Selector "initWithAttributedString:" (receiver As Ptr, str As Ptr) As Ptr attrString = initWithString(attrString, pnt2) Declare Sub beginEditing Lib "Cocoa" Selector "beginEditing" (receiver As Ptr) beginEditing(attrString) Declare Sub insertAttributedString Lib "Cocoa" Selector "insertAttributedString:atIndex:" (receiver As Ptr, attachChar As Ptr, index As Integer) insertAttributedString(attrString, attachChar, TextArea1.SelStart) // Xojo側からキャレット位置を取得。NSTextView側から取得した方がいいかも。 Declare Sub endEditing Lib "Cocoa" Selector "endEditing" (receiver As Ptr) endEditing(attrString) // 画像を追加したストレージを書き戻す Declare Sub setAttributedString Lib "Cocoa" Selector "setAttributedString:" (receiver As Ptr, attrString As Ptr) setAttributedString(pnt2, attrString) end if
- 以下をToolbar11のActionイベントに追加
select case item.Name case "ToolItem1" // 開く OpenFile() case "ToolItem2" // 保存 SaveFile() case "ToolItem4" // 表 // NSTextViewの取得 Dim pnt1 As Ptr declare function documentView lib "Cocoa" selector "documentView" (obj_id as Integer) as Ptr // Return NSTextView* pnt1 = documentView(TextArea1.Handle) // 表ダイアログを表示 declare sub orderFrontTablePanel lib "Cocoa" selector "orderFrontTablePanel:" (receiver as Ptr, sender as Ptr) orderFrontTablePanel(pnt1, nil) case "ToolItem5" // リンク // NSTextViewの取得 Dim pnt1 As Ptr declare function documentView lib "Cocoa" selector "documentView" (obj_id as Integer) as Ptr // Return NSTextView* pnt1 = documentView(TextArea1.Handle) // リンクダイアログを表示 declare sub orderFrontLinkPanel lib "Cocoa" selector "orderFrontLinkPanel:" (receiver as Ptr, sender as Ptr) orderFrontLinkPanel(pnt1, nil) end select
実行してみたところ、リッチテキストの編集、ファイルの読み書きが機能することを確認しました。
おわりに
NSTextViewは他にも大量のメソッドが用意されてます。アンドゥ等の機能もある?ようなので、機会があればトライしてみたいと思います。については、こちらのページに纏めました。
最後に毎度の繰り返しですが、実用目的であれば、macoslib(当然のごとく、対応している)をそのまま利用させて頂くことを考えた方がいいでしょう。
追記:インスペクターバーの制約について、こちらのページに纏めました。
お世話になったサイト
貴重な情報をご提供頂いている皆様に、お礼申し上げます。(以下、順不同)
参考サイト(1):添付書類付きリッチテキスト(.rtfd)の実態 - ザリガニが見ていた...。
参考サイト(2):24 | 3月 | 2008 | QueueSoft Developing Diary
更新履歴
2017.08.15 Xojoでの実装の5項に、「インスペクターバー付加によるHeightの増加に、ウィンドウが追随しないことへの対策」を追加。
2016.05.01 Xojoでの実装の9項に、注を追加
2016.04.05 おわりにの記述に、別記事へのリンクを追加
2016.03.19 おわりにの記述に、追記を追加
2015.11.03 新規作成
[Home] [MacSoft] [Donation] [History] [Privacy Policy] [Affiliate Policy]