ホームページ>開発ツール>Xojo / Real Studio Trial and Error・CocoaのDeclareでファイルIOパネルを拡張する・Xojoのエディタを使う
Xojo / Real Studio Trial and Error
目次
CocoaのDeclareでファイルIOパネルを拡張する・Xojoのエディタを使う
はじめに
以下は、Xojo Cocoaビルドについての話題です。
前回まではaccessoryView(以下、パラメータパネル)の生成にあたり、全てコードで記述していましたが、Xojoのレイアウトエディタが使えないか、調べてみました。
なお検証には、Xojo 2017 Release 1.1を用いています。(Mac mini 2018 + macOS 10.14.6 Mojave)
方針
初めに、パラメータパネルの貼り付け元を限定しておきます。ここでは、前項で試行した「シート形式で、Blocksを構造体で実装」とします。
さて、ハイブリッド化にあたり、まず考えることは、どのコントロールが相応しい(使える)か?という点でしょうか。
とはいえ、現状、コントロールを内包できてエディタで編集可能なのは、ウィンドウかContainer Control位しか見当たりません。
(Tab PanelやPage Panelは、サブクラス化してもエディタで編集できない。)
そこで、まずはContainer Controlの親クラス名を調べてみました。すると、NSViewでした。
これなら、setAccessoryView:で貼り付けられそうです。
実験してみたところ、問題なさそうでしたので、これを使うこととしました。
内包するコントロール類ですが、試した範囲では、Button(NSButton)、RadioButton(NSButton)、CheckBox(NSButton)、Popup Menu(NSPopUpButton)、TextField(NSTextField)、は機能しました。
BevelButton(NSView)は押しても反応しないので、別のものにする必要があります。(外観だけならButtonで代用できます。)
次に、Container Controlの貼り付け方ですが、当初はnewした直後にsetAccessoryView:すればいいかと思ったのですが、これではダメで、EmbedWithin()しないとインスタンスが作られませんでした。
なので一旦、(シートを表示する)ウィンドウにEmbedWithin()した直後に、シートにsetAccessoryView:することにしました。
(この場合、コピーではなく移動になります。なお、移動に際して、ちらつき等は見受けられませんでした。)
パラメータの取得は、前回は、ボタンが押された後に呼び出されるメソッド内で行ってきましたが、今回は、パスは従来通り(あらかじめ保持しておいた)シートから取得しますが、コントロール値の取得はこのメソッド内では行わず、(こちらもあらかじめ保持しておいた)Container Controlをダイアログ生成元(で指定したメソッド)に引数で渡して、そちらで取得することとしました。これで、引数の汎用化を図ることができます。
以上を踏まえ、(残りの)仕様は以下の通りとしました。
- パネル種別は、OpenとSaveの両方に対応する
- OpenPanelのパラメータは、テキストエンコーディング(ポップアップメニュー:Shift-JIS/UTF-8/UTF-16)、区切り記号(ラジオボタン:カンマ/タブ)
- SavePanelのパラメータは、画像形式(ポップアップメニュー:JPEG/PNG/TIFF)、コメント(テキストフィールド)、オプション(チェックボックス)
- Container Controlを引数で渡す際の型はVariantとし、受け取り先でキャストして復元する
- ファイル選択ダイアログ関連処理はクラス化する
Xojoでの実装
【ソースコードのコピー&ペーストについて】
ソースコード(グレー背景部分の全文)をコピーし、指定のウィンドウ/クラスにペーストすると、(新規作成して名前等を個別にコピー&ペーストしなくても)復元されます。
ただし、この方法は、メソッドでは問題ないようですが、イベント/アクション/プロパティでは不安定?なので、ペーストできない場合は、各項目のカッコ内を適用して下さい。
実行してみたところ、ファイルオープン/保存シートにパラメータパネルが表示され、パラメータが取得できることを確認しました。
- Xojoで新規プロジェクトを作成
- Window1にListBox(ListBox1)を置く。
- Window1にPushButtonを2個(PushButton1〜2)置く。
- 以下をPushButton1にペースト(できなければ、Sub - Endの間をActionイベントに記述)
Sub Action() Handles Action Import() End Sub
- 以下をPushButton2にペースト(できなければ、Sub - Endの間をActionイベントに記述)
Sub Action() Handles Action Export() End Sub
- 以下をWindow1にペースト
Protected Sub Export() // パラメータパネルの生成 Dim cc As ContainerControl2 = new ContainerControl2 // 初期値セット cc.PopupMenu1.ListIndex=1 // AccessoryPanelクラスの生成(パネル種別、親ウィンドウ、パラメータパネル、ボタンActionを受け取るメソッド、を引数で指定する) Dim aos As NSOpenSavePanel = new NSOpenSavePanel(2, self, cc, AddressOf ExportDone) End Sub
- 以下をWindow1にペースト
Private Sub ExportDone(rcode As Integer, path As String, param As Variant) Listbox1.DeleteAllRows // キャンセルボタンが押されたら戻る if rcode=0 then return end if // ファイルの取得 Dim f As FolderItem = GetFolderItem(path,3) if f=nil then return end if // ダイアログから渡ってきたパラメータパネルを取得(Variantで渡ってくるのでキャストする) Dim cc As ContainerControl2 = param // パラメータを表示 Listbox1.AddRow cc.PopupMenu1.Text Listbox1.AddRow cc.TextField1.Text if cc.CheckBox1.Value then Listbox1.AddRow "チェックあり" else Listbox1.AddRow "チェックなし" end if End Sub
- 以下をWindow1にペースト
Protected Sub Import() // パラメータパネルの生成 Dim cc As ContainerControl1 = new ContainerControl1 // 初期値セット cc.PopupMenu1.ListIndex=1 // AccessoryPanelクラスの生成(パネル種別、親ウィンドウ、パラメータパネル、ボタンActionを受け取るメソッド、を引数で指定する) Dim aos As NSOpenSavePanel = new NSOpenSavePanel(1, self, cc, AddressOf ImportDone) End Sub
- 以下をWindow1にペースト
Private Sub ImportDone(rcode As Integer, path As String, param As Variant) Listbox1.DeleteAllRows // キャンセルボタンが押されたら戻る if rcode=0 then return end if // ファイルの取得 Dim f As FolderItem = GetFolderItem(path,3) if f=nil or f.Exists=false then return end if // ダイアログから渡ってきたパラメータパネルを取得(Variantで渡ってくるのでキャストする) Dim cc As ContainerControl1 = param // パラメータを表示 Listbox1.AddRow cc.PopupMenu1.Text if cc.RadioButton1.Value then Listbox1.AddRow "カンマ" elseif cc.RadioButton2.Value then Listbox1.AddRow "タブ" end if End Sub
- 新規クラスを作成(名前は、ここでは「NSOpenSavePanel」とした。)
- 以下をNSOpenSavePanelにペースト(できなければ移譲に、名前:ActionDelegate、引数:rcode As Integer, path As String, dict As Variant、を追加)
Private Sub ActionDelegate(rcode As Integer, path As String, dict As Variant)
- 以下をNSOpenSavePanelにペースト
Public Sub Constructor(kind As Integer, win As Window, cc As ContainerControl, action As ActionDelegate) // インスタンス側でActionを受け取るメソッドを登録 ActionHandler = action // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // ダイアログの生成 select case kind case 1 // OpenPanelの生成 Panel = NSClassFromString("NSOpenPanel") Declare Function openPanel Lib "Cocoa" selector "openPanel" (class_id As Ptr) As Ptr Panel = openPanel(Panel) case 2 // SavePanelの生成 Panel = NSClassFromString("NSSavePanel") Declare Function savePanel Lib "Cocoa" selector "savePanel" (class_id As Ptr) As Ptr Panel = savePanel(Panel) end select // パラメータパネルのセット cc.EmbedWithin(win) Declare Sub setAccessoryView Lib "Cocoa" selector "setAccessoryView:" (class_id As Ptr, view As Integer) setAccessoryView(Panel, cc.Handle) PanelContainer = cc // シート生成(Blocks構造体) Declare Sub beginSheetModalForWindow Lib "Cocoa" Selector "beginSheetModalForWindow:completionHandler:" (receiver As Ptr, win As Integer, byRef block As Block_layout) blk.isa_ = NSClassFromString("__NSGlobalBlock__") blk.invoke = AddressOf didEndSheetCH beginSheetModalForWindow(Panel, win.Handle, blk) End Sub
- 以下をNSOpenSavePanelにペースト
Private Shared Sub didEndSheetCH(obj As Ptr, rcode As integer) // rcode = 0 : Cancel // rcode = 1 : OK Dim path As String = "" if rcode=1 then // OK // 選択したファイルのパス取得 Declare Function URL Lib "Cocoa" selector "URL" (class_id As Ptr) As Ptr Dim pnt1 As Ptr = URL(Panel) Declare Function myPath Lib "Cocoa" selector "path" (class_id As Ptr) As CFStringRef path = myPath(pnt1) else // Cancel end if // インスタンス側でActionを受け取るメソッドをレイズする ActionHandler.Invoke(rcode, path, PanelContainer) End Sub
- 以下をNSOpenSavePanelにペースト(できなければ共有プロパティに、名前:ActionHandler、データ型:ActionDelegate、を追加)
Private Shared Property ActionHandler as ActionDelegate
- 以下をNSOpenSavePanelにペースト(できなければ共有プロパティに、名前:blk、データ型:Block_layout、を追加)
Private Shared Property blk as Block_layout
- 以下をNSOpenSavePanelにペースト(できなければ共有プロパティに、名前:Panel、データ型:Ptr、を追加)
Private Shared Property Panel as Ptr
- 以下をNSOpenSavePanelにペースト(できなければ共有プロパティに、名前:PanelContainer、データ型:Variant、を追加)
Private Shared Property PanelContainer as Variant
- 新規ContainerControlを2個作成(名前は、ここではデフォルトの「ContainerControl1」「ContainerControl2」とした。)
(注:IDEの右ペイン(ライブラリ)にあるContainerControlを、左ペイン(ナビゲーター)にドラッグ&ドロップする。)- ContainerControl1に、GroupBox(Name:GroupBox1)、PopupMenu(Name:PopupMenu1)、RadioButton(Name:RadioButton1)、RadioButton(Name:RadioButton2)を追加
- ContainerControl2に、CheckBox(Name:CheckBox1)、PopupMenu(Name:PopupMenu1)、TextField(Name:TextField1)を追加
おわりに
コントロールの位置/サイズが、グラフィカルに微調整できるのは楽です。
かといって、あまり安易にコントロールを増やすのは好ましくなさそうなので、注意が必要かと思われます。
お世話になったサイト
貴重な情報をご提供頂いている皆様に、お礼申し上げます。(以下、順不同)
更新履歴
2020.01.14 新規作成
[Home] [MacSoft] [Donation] [History] [Privacy Policy] [Affiliate Policy]