ホームページ開発ツール>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をダイアログ生成元(で指定したメソッド)に引数で渡して、そちらで取得することとしました。これで、引数の汎用化を図ることができます。

 以上を踏まえ、(残りの)仕様は以下の通りとしました。

 Xojoでの実装
【ソースコードのコピー&ペーストについて】
ソースコード(グレー背景部分の全文)をコピーし、指定のウィンドウ/クラスにペーストすると、(新規作成して名前等を個別にコピー&ペーストしなくても)復元されます。
ただし、この方法は、メソッドでは問題ないようですが、イベント/アクション/プロパティでは不安定?なので、ペーストできない場合は、各項目のカッコ内を適用して下さい。
  1. Xojoで新規プロジェクトを作成
  2. Window1にListBox(ListBox1)を置く。
  3. Window1にPushButtonを2個(PushButton1〜2)置く。
  4. 以下をPushButton1にペースト(できなければ、Sub - Endの間をActionイベントに記述)
    Sub Action() Handles Action
      Import()
    End Sub
    
  5. 以下をPushButton2にペースト(できなければ、Sub - Endの間をActionイベントに記述)
    Sub Action() Handles Action
      Export()
    End Sub
    
  6. 以下を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
    
  7. 以下を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
    
  8. 以下を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
    
  9. 以下を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
    
  10. 新規クラスを作成(名前は、ここでは「NSOpenSavePanel」とした。)
  11. 以下をNSOpenSavePanelにペースト(できなければ移譲に、名前:ActionDelegate、引数:rcode As Integer, path As String, dict As Variant、を追加)
    Private Sub ActionDelegate(rcode As Integer, path As String, dict As Variant)
    
  12. 以下を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
    
  13. 以下を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
    
  14. 以下をNSOpenSavePanelにペースト(できなければ共有プロパティに、名前:ActionHandler、データ型:ActionDelegate、を追加)
    Private Shared Property ActionHandler as ActionDelegate
    
  15. 以下をNSOpenSavePanelにペースト(できなければ共有プロパティに、名前:blk、データ型:Block_layout、を追加)
    Private Shared Property blk as Block_layout
    
  16. 以下をNSOpenSavePanelにペースト(できなければ共有プロパティに、名前:Panel、データ型:Ptr、を追加)
    Private Shared Property Panel as Ptr
    
  17. 以下をNSOpenSavePanelにペースト(できなければ共有プロパティに、名前:PanelContainer、データ型:Variant、を追加)
    Private Shared Property PanelContainer as Variant
    
  18. 新規ContainerControlを2個作成(名前は、ここではデフォルトの「ContainerControl1」「ContainerControl2」とした。)
    (注:IDEの右ペイン(ライブラリ)にあるContainerControlを、左ペイン(ナビゲーター)にドラッグ&ドロップする。)
  19. ContainerControl1に、GroupBox(Name:GroupBox1)、PopupMenu(Name:PopupMenu1)、RadioButton(Name:RadioButton1)、RadioButton(Name:RadioButton2)を追加
    S Shot1
  20. ContainerControl2に、CheckBox(Name:CheckBox1)、PopupMenu(Name:PopupMenu1)、TextField(Name:TextField1)を追加
    S Shot2
 実行してみたところ、ファイルオープン/保存シートにパラメータパネルが表示され、パラメータが取得できることを確認しました。
S Shot3


 おわりに

 コントロールの位置/サイズが、グラフィカルに微調整できるのは楽です。
 かといって、あまり安易にコントロールを増やすのは好ましくなさそうなので、注意が必要かと思われます。


 お世話になったサイト

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


 更新履歴

 2020.01.14 新規作成


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