ホームページ>開発ツール>Xojo / Real Studio Trial and Error・CocoaのDeclareでリストボックスのセル種別を増やしてみる(改訂版)
Xojo / Real Studio Trial and Error
目次
CocoaのDeclareでリストボックスのセル種別を増やしてみる(改訂版)
はじめに
以下は、Xojo Cocoaビルドについての話題です。
Xojo標準のリストボックスのセル種別は、テキストとチェックボックスだけですが、これを増やすことができないか試してみました。
なお検証には、Xojo 2016 Release 3を用いています。(Mac mini mid 2010 + OS X 10.13.6 High Sierra)
注)初版は、ファイルから読み込んだ行がリストボックスの高さを超える(即ち、縦スクロールが発生する)と、(スクロールして)隠れていたセルを表示しても、値が未セットになってしまう問題(以下、スクロール問題)が確認されたため、対応した改訂版としました。(旧版はこちら)
方針
当初は、XojoのListBoxクラスを拡張すればいいか、と軽く考えていたのですが、調べてみるとXojoのListBox(XOJListboxView)はNSTableViewではなく、NSViewのサブクラスであることが分かりました。
つまり、クラス拡張の手は使えませんので、(仕方なく)NSTableViewのサブクラスを独自に実装することしました。
さて、NSTableViewにはCell BaseとView Baseの二つの方法があるとのことで、現在はView Baseが推奨されているようです。
参考サイト(1):NSCellを使ってはいけない - Qiita
参考サイト(2):NSTableView - src256 wiki
なので、View Baseでいくことにします。(注:NSTableViewがView Baseに対応したのは10.7 Lionとのことなので、動作環境はそれ以降となります。)
ただ、Cell Baseが非推奨になったとはいえ、ネット上にはCell Baseの情報の方が多いので、参照する場合には注意が必要です。(当初は違いがよく判らず、混乱しました。)
NSTableViewでのデータハンドリングの特徴(の一つ)は、セルとは別にKey-Value Codingに対応したデータ(以下、内部データ)を持つ点ではなかろうかと思われます。
Cell Baseでは、tableView:dataCellForTableColumn:row:でオブジェクトをセットしておき、セルを移動したりするとtableView:objectValueForTableColumn:row:が呼ばれるのですが、ここで内部データを返す必要があるため、セルを編集した場合はこのメソッドが呼ばれるまでの間に、内部データに書き戻す必要が生じます。
一方View Baseでは、tableView:viewForTableColumn:row:でオブジェクトと値をセットしますが、セル移動時には(確認できた範囲内では)何も呼ばれません。
そのため、内部データを省略することもできてしまうので、当初は内部データを作らない方式を試したのですが、スクロール問題が発生したため、(正攻法である?)内部データを保持する方式とします。(ただし、Key-Value Codingは意識していません。)
次にViewの中身ですが、チェックボックスを含むNSButtonは、alloc/initしてボタンタイプを指定するやり方は既にDeprecatedになっていて、別途メソッドが用意されていますが、ここでは(10.14までは有効なこともあり)他と合わせる意味もあって、alloc/init方式としました。
なお、新方式を利用する場合は、リセット時のreleaseは行わないよう、対策する必要があります。(allocしていないので、releaseすると異常終了する。)
また、ネット上のサンプルは、多くがインターフェイスビルダーを使う前提で書かれているのですが、Xojoでは使えませんので、全てコードで書くにあたって、以下を参考にさせて頂きました。(注:Cell Baseなので、そのままの適用は不可。)
参考サイト(3):NSTableView (Creation and Loading from Code) – Knowledge Stack
以上を踏まえ、(残りの)仕様は以下の通りとしました。
- コントロールの種類は、テキスト/チェックボックス/ポップアップメニュー/セグメンテッドコントロール/スライダー/レベルインジケーター
- データをファイルから読み込み/ファイルに書き出し可能
- ファイル形式はCSVライクなテキストファイル(詳細はこちら)
- 列と行の追加可能(追加できる列の種別はテキストのみ。必要ならカスタマイズして下さい。)
- 列のリサイズ/並べ替え/ヘッダープレスによる行ソート可能
- 値の変更は、テキストはcontrol:textShouldEndEditing:、それ以外はTarget/Actionで検出し、内部データを更新する。
- NSTableView関連の処理は、クラス化する
- numberOfRowsInTableView:は全てのインスタンスに共通(常に0を返す)なので、クラスメソッド(共有メソッド)内で処理する
Xojoでの実装
【ソースコードのコピー&ペーストについて】
ソースコード(グレー背景部分の全文)をコピーし、指定のウィンドウ/クラスにペーストすると、(新規作成して名前等を個別にコピー&ペーストしなくても)復元されます。
ただし、この方法は、メソッドでは問題ないようですが、イベント/アクション/プロパティでは不安定?なので、ペーストできない場合は、各項目のカッコ内を適用して下さい。
実行してみたところ、指定したコントロールがリストボックスのセルに追加され、機能することを確認しました。
- Xojoで新規プロジェクトを作成
- Toolbarをプロジェクトに追加(Name:Toolbar1)後、以下のアイテムを追加。その後、Window1に追加(Name:Toolbar11)。
ToolItem1(開く)/ToolItem2(保存)/ToolItem3(スペース)/ToolItem4(一行追加)/ToolItem5(一列追加)- 以下をToolbar11にペースト(できなければ、Sub - Endの間をToolbar11のActionイベントに記述)
Sub Action(item As ToolItem) Handles Action select case Item.Name case "ToolItem1" LoadFile() case "ToolItem2" SaveFile() case "ToolItem4" Dim colCnt As Integer = GetColCount() Dim rowData(-1) As String ReDim rowData(colCnt) // 列数分の空の行データ AddRow(rowData) case "ToolItem5" AddCol("col "+Str(GetColMaxVal()),150,"text") end select End Sub
- 以下をWindow1にペースト(できなければ、Sub - Endの間をOpenイベントに記述)
Sub Open() Handles Open // NSTableView。引数は順に、生成したインスタンス、配置するウィンドウ、位置/サイズ、Delegateメソッド Dim d As NSViewListBox = new NSViewListBox(tableviewInst, self, NSMakeRect(16, 16, me.Width-32, me.Height-32), _ AddressOf actionEvent, AddressOf viewForTable, AddressOf sortDescriptors, AddressOf didClickTableColumn, AddressOf textEndEdit) // 内部データの初期化 InitDataArray() End Sub
- 以下をWindow1にペースト
Protected Sub actionEvent(id As Ptr, sel as CString, sender As Ptr) // 変更後の値を内部データに反映させる actionEventValue(id,sender) End Sub
- 以下をWindow1にペースト
Protected Sub actionEventValue(tableView As Ptr, cellView As Ptr) // 値を変更したセルの行位置を取得 Declare Function rowForView Lib "Cocoa" Selector "rowForView:" (receiver As Ptr, view As Ptr) As Integer Dim row As Integer = rowForView(tableView, cellView) // 値を変更したセルの列位置を取得 Declare Function columnForView Lib "Cocoa" Selector "columnForView:" (receiver As Ptr, view As Ptr) As Integer Dim col As Integer = columnForView(tableView, cellView) // 全ての列を取得 Declare Function tableColumns Lib "Cocoa" Selector "tableColumns" (receiver As Ptr) As Ptr Dim colAry As Ptr = tableColumns(tableView) if colAry=nil then // 取得できなければ戻る return end if // 列クラスを取得 Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, w As Integer) As Ptr Dim tCol As Ptr = objectAtIndex(colAry, col) // 列のアイデンティファイアを取得 Declare Function identifier Lib "Cocoa" Selector "identifier" (receiver As Ptr) As CFStringRef Dim ident As String = identifier(tCol) // アイデンティファイアから列種別と固有化番号を取得 Dim kind As String = NthField(ident,":",1) Dim colNo As Integer = Val(NthField(ident,":",2)) Dim st As String select case kind case "text" // 文字列 // 文字列を取得 Declare Function stringValue Lib "Cocoa" Selector "stringValue" (receiver As Ptr) As CFStringRef st=stringValue(cellView) case "chkb" // チェックボックス // Stateを取得 Declare Function state Lib "Cocoa" Selector "state" (receiver As Ptr) As Integer st=Str(state(cellView)) case "popu" // ポップアップメニュー // ListIndexを取得 Declare Function indexOfSelectedItem Lib "Cocoa" Selector "indexOfSelectedItem" (receiver As Ptr) As Integer st=Str(indexOfSelectedItem(cellView)) case "segm" // セグメンテッドコントロール // Segmentを取得 Declare Function selectedSegment Lib "Cocoa" Selector "selectedSegment" (receiver As Ptr) As Integer st=Str(selectedSegment(cellView)) case "slid" // スライダー // スライダー値を取得 Declare Function doubleValue Lib "Cocoa" Selector "doubleValue" (receiver As Ptr) As Double st=Str(doubleValue(cellView)) case "indi" // レベルインジケーター // インジケーター値を取得 Declare Function doubleValue Lib "Cocoa" Selector "doubleValue" (receiver As Ptr) As Double st=Str(doubleValue(cellView)) end select // 行データを取得 'Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr Dim objct As Ptr = objectAtIndex(dataArrayNS, row) // 列アイデンティファイアをキーにして、Dictionaryの値を変更 Declare Sub setObject Lib "Cocoa" Selector "setObject:forKey:" (receiver As Ptr, obj As CFStringRef, key As CFStringRef) setObject(objct, st, ident) End Sub
- 以下をWindow1にペースト
Protected Sub AddCol(title As String, width As CGFloat, kind As String) // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr // 列クラス(NSTableColumn)のインスタンスを生成 Dim ident As String = kind+":"+Str(GetColMaxVal()) Dim tCol As Ptr = NSClassFromString("NSTableColumn") // クラスメソッドなので、まずNSTableColumnクラスを取得 Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr Declare Function initWithIdentifier Lib "Cocoa" selector "initWithIdentifier:" (obj_id As Ptr, ifer As CFStringRef) As Ptr tCol = initWithIdentifier(alloc(tCol), ident) // 内部データ生成 AddColDictionary(ident) // 列幅をセット Declare Sub setWidth Lib "Cocoa" Selector "setWidth:" (receiver As Ptr, w As CGFloat) setWidth(tCol, width) // タイトルをセット Declare Sub setTitle Lib "Cocoa" Selector "setTitle:" (receiver As Ptr, name As CFStringRef) setTitle(tCol, title) // ヘッダプレスによるソートを許可 Dim pnt1 As Ptr = NSClassFromString("NSSortDescriptor") // クラスメソッドなので、まずNSSortDescriptorクラスを取得 Declare Function sortDescriptor Lib "Cocoa" Selector "sortDescriptorWithKey:ascending:selector:" (receiver As Ptr, obj1 As CFStringRef, obj2 As Boolean, obj3 As Ptr) As Ptr pnt1 = sortDescriptor(pnt1, ident, true, NSSelectorFromString("compare:")) Declare Sub setSortDescriptors Lib "Cocoa" Selector "setSortDescriptorPrototype:" (receiver As Ptr, typ As Ptr) setSortDescriptors(tCol, pnt1) // 列クラスのインスタンスをテーブルビューに追加 Declare Sub addTableColumn Lib "Cocoa" Selector "addTableColumn:" (receiver As Ptr, col As Ptr) addTableColumn(tableViewInst, tCol) // clean up Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr) release(tCol) End Sub
- 以下をWindow1にペースト
Protected Sub AddColDictionary(ident As String) // 行のループ for row As Integer = 0 to rowMax-1 // 行データDictionaryの取得 Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr Dim rowDict As Ptr = objectAtIndex(dataArrayNS, row) // 列アイデンティファイアをキーにして、行データDictionaryに追加 Declare Sub setObject Lib "Cocoa" Selector "setObject:forKey:" (receiver As Ptr, obj As CFStringRef, key As CFStringRef) setObject(rowDict, "", ident) // データは空 next End Sub
- 以下をWindow1にペースト
Protected Sub AddRow(rowData() As String) // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // 内部データ生成 AddRowDictionary(rowData) // NSIndexSet生成 Dim indexes As Ptr = NSClassFromString("NSMutableIndexSet") // クラスメソッドなので、まずNSMutableIndexSetクラスを取得 Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr Declare Function init Lib "Cocoa" selector "init" (obj_id As Ptr) As Ptr indexes = init(alloc(indexes)) Declare Sub addObject Lib "Cocoa" Selector "addIndex:" (receiver As Ptr, val As Integer) addObject(indexes, rowMax) // 行を追加 Declare Sub insertRows Lib "Cocoa" Selector "insertRowsAtIndexes:withAnimation:" (receiver As Ptr, idxset As Ptr, anime As Integer) insertRows(tableViewInst, indexes, 0) // clean up Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr) release(indexes) // 行数をカウントアップ rowMax=rowMax+1 End Sub
- 以下をWindow1にペースト
Protected Sub AddRowDictionary(dataArray() As String) // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Dim dict1 As Ptr = NSClassFromString("NSMutableDictionary") Declare Function dictionary Lib "Cocoa" Selector "dictionary" (receiver As Ptr) As Ptr dict1 = dictionary(dict1) // 列集合を取得 Declare Function tableColumns Lib "Cocoa" Selector "tableColumns" (receiver As Ptr) As Ptr Dim colAry As Ptr = tableColumns(tableviewInst) // 列数を取得 Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer Dim colCnt As Integer = count(colAry) Dim col As Integer Dim st As String for col=0 to colCnt-1 // 列のループ // 列を順に取得 Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr Dim column As Ptr = objectAtIndex(colAry, col) // 列アイデンティファイア Declare Function identifier Lib "Cocoa" selector "identifier" (class_id As Ptr) As CFStringRef Dim ident As String = identifier(column) // 値をセット st=dataArray(col) // 列アイデンティファイアをキーにして、Dictionaryに追加 Declare Sub setObject Lib "Cocoa" Selector "setObject:forKey:" (receiver As Ptr, obj As CFStringRef, key As CFStringRef) setObject(dict1, st, ident) next // 行配列に追加 Declare Sub addObject Lib "Cocoa" Selector "addObject:" (receiver As Ptr, obj As Ptr) addObject(dataArrayNS, dict1) End Sub
- 以下をWindow1にペースト
Protected Sub didClickTableColumn(id as Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr) // SortDescriptor生成 Dim dsc As Ptr = GetSortDesc(tableColumn) // ソート declare sub sortUsingDescriptors lib "Cocoa" Selector "sortUsingDescriptors:" (obj as ptr, desc as Ptr) sortUsingDescriptors(dataArrayNS, dsc) // ソート後の内部データをセルに書き戻す SetSortedData(dataArrayNS) End Sub
- 以下をWindow1にペースト
Protected Function GetClassName(cls As Ptr) as String // クラスを取得 Declare Function myClass Lib "Cocoa" selector "class" (class_id As Ptr) As Ptr Dim pnt1 As Ptr = myClass(cls) // クラス名を取得 Declare Function NSStringFromClass Lib "Cocoa" (aClass As Ptr) As CFStringRef Dim cnam As String = NSStringFromClass(pnt1) // クラス名を返す return cnam End Function
- 以下をWindow1にペースト
Protected Function GetColCount() as Integer // 列集合を取得 Declare Function tableColumns Lib "Cocoa" Selector "tableColumns" (receiver As Ptr) As Ptr Dim colAry As Ptr = tableColumns(tableviewInst) // 列数を取得 Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer Dim colCnt As Integer = count(colAry) // 列数を返す return colCnt End Function
- 以下をWindow1にペースト
Protected Function GetColMaxVal() as Integer // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // データ領域の生成 Dim dataArray As Ptr dataArray = NSClassFromString("NSMutableArray") // クラスメソッドなので、まずNSMutableArrayクラスを取得 Declare Function getArray Lib "Cocoa" Selector "array" (receiver As Ptr) As Ptr // Return Array* dataArray = getArray(dataArray) // 列集合を取得 Declare Function tableColumns Lib "Cocoa" Selector "tableColumns" (receiver As Ptr) As Ptr Dim colAry As Ptr = tableColumns(tableviewInst) // 列数を取得 Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer Dim colCnt As Integer = count(colAry) // 列のループ Dim col, colmax As Integer for col=0 to colCnt-1 // 列を順に取得 Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr Dim column As Ptr = objectAtIndex(colAry, col) // 列アイデンティファイア Declare Function identifier Lib "Cocoa" selector "identifier" (class_id As Ptr) As CFStringRef Dim ident As String = identifier(column) // 固有化番号を取得 Dim colNo As Integer = Val(NthField(ident,":",2)) // 固有化番号の最大値を保持 if colNo>colMax then colMax=colNo end if next // 固有化番号の最大値の次の値を返す return colMax+1 End Function
- 以下をWindow1にペースト
Protected Function GetSortDesc(tableColumn As Ptr) as Ptr // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr // 列アイデンティファイアの取得 Declare Function identifier Lib "Cocoa" selector "identifier" (class_id As Ptr) As CFStringRef Dim ident As String = identifier(tableColumn) // ソート順(昇順/降順)のセット Dim ascend As Boolean = true if oldDescs<>nil then // 初回はoldDescs=nilなので除く(trueが渡る) // oldDescsの要素分ループ Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer Dim cnt As Integer = count(oldDescs) for i As Integer = 0 to cnt-1 // 要素を順に取得 Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, w As Integer) As Ptr Dim odesc As Ptr = objectAtIndex(oldDescs, i) // キー(アイデンティファイア)を取得 Declare Function myKey Lib "Cocoa" Selector "key" (receiver As Ptr) As CFStringRef Dim ident2 As String = myKey(odesc) // 前回のデスクリプターが見つかったら if ident=ident2 then Declare Function ascending Lib "Cocoa" Selector "ascending" (receiver As Ptr) As Boolean if ident<>previousCol then // 異なる列を選択した場合、ソートマークは変化しないので、前回の値をそのまま使用 ascend = ascending(odesc) else // 同じ列を選択した場合、ソートマークが変化するので、前回の値を反転して使用 ascend = not ascending(odesc) end if exit end if next end if // SortDescriptorを生成 Dim pnt1 As Ptr = NSClassFromString("NSSortDescriptor") // クラスメソッドなので、まずNSSortDescriptorクラスを取得 Declare Function sortDescriptor Lib "Cocoa" Selector "sortDescriptorWithKey:ascending:selector:" (receiver As Ptr, key As CFStringRef, asc As Boolean, sel As Ptr) As Ptr pnt1 = sortDescriptor(pnt1, ident, ascend, NSSelectorFromString("compare:")) // 配列を生成 Dim arr As Ptr = NSClassFromString("NSMutableArray") // クラスメソッドなので、まずNSMutableArrayクラスを取得 Declare Function getArray Lib "Cocoa" Selector "array" (receiver As Ptr) As Ptr // Return Array* arr = getArray(arr) // SortDescriptorを追加 Declare Sub addObject Lib "Cocoa" Selector "addObject:" (receiver As Ptr, obj As Ptr) addObject(arr, pnt1) // ヘッダをプレスした列のアイデンティファイアを保持 previousCol=ident // リストを返す return arr End Function
- 以下をWindow1にペースト
Protected Sub InitDataArray() // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // 参照数の取得 Declare Function retainCount Lib "Cocoa" selector "retainCount" (class_id As Ptr) As Integer Dim cnt As Integer = retainCount(dataArrayNS) // 参照があったら、解放する if cnt>0 then // clean up Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr) release(dataArrayNS) end if // データ領域の生成 dataArrayNS = NSClassFromString("NSMutableArray") // クラスメソッドなので、まずNSMutableArrayクラスを取得 Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr Declare Function init Lib "Cocoa" selector "init" (obj_id As Ptr) As Ptr dataArrayNS = init(alloc(dataArrayNS)) End Sub
- 以下をWindow1にペースト
Protected Sub LoadFile() Dim f As FolderItem Dim tis As TextInputStream Dim line As String // ファイルオープンダイアログ f=GetOpenFolderItem("text/plain") // defined as a FileType if f=nil or not f.Exists then return end if tis=TextInputStream.Open(f) // ファイルを開く if tis=nil then return end if tis.Encoding=Encodings.UTF8 // テキストエンコーディングはUTF-8 // まず、初期化 ResetTable() line=tis.ReadLine // 一ライン読み込み Dim colTitle(-1) As String colTitle=Split(line, ",") // 列タイトル line=tis.ReadLine // 一ライン読み込み Dim colWidth(-1) As String colWidth=Split(line, ",") // 列幅 line=tis.ReadLine // 一ライン読み込み Dim colKind(-1) As String colKind=Split(line, ",") // 列種別 // 列生成 for i As Integer = 0 to colTitle.Ubound AddCol(colTitle(i),Val(colWidth(i)),colKind(i)) next // ラインの終りまで繰り返す rowMax=0 while not tis.EOF line=tis.ReadLine // 一ライン読み込み Dim rowData(-1) As String rowData=Split(line, ",") // 一行分のセルデータ配列 AddRow(rowData) // 一行追加 wend // ストリームのクローズ tis.Close End Sub
- 以下をWindow1にペースト
Protected Function makeMenuArray() as Ptr // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Dim arr As Ptr = NSClassFromString("NSMutableArray") // クラスメソッドなので、まずNSMutableArrayクラスを取得 Declare Function getArray Lib "Cocoa" Selector "array" (receiver As Ptr) As Ptr // Return Array* arr=getArray(arr) Declare Sub addObject Lib "Cocoa" Selector "addObject:" (receiver As Ptr, obj As CFStringRef) addObject(arr, "popup 0") addObject(arr, "popup 1") addObject(arr, "popup 2") addObject(arr, "popup 3") addObject(arr, "popup 4") addObject(arr, "popup 5") addObject(arr, "popup 6") addObject(arr, "popup 7") addObject(arr, "popup 8") addObject(arr, "popup 9") addObject(arr, "popup 10") // メニューリストを返す return arr End Function
- 以下をWindow1にペースト
Protected Sub RemoveAllCol() // 全ての列を取得 Declare Function tableColumns Lib "Cocoa" Selector "tableColumns" (receiver As Ptr) As Ptr Dim colAry As Ptr = tableColumns(tableviewInst) // 取得できなければ戻る if colAry=nil then return end if // 列数を取得 Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer Dim colCnt As Integer = count(colAry) // 列がなければ戻る if colCnt<=0 then return end if // 列を削除(最後から消していく) for i As Integer = colCnt-1 downto 0 Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, w As Integer) As Ptr Dim tCol As Ptr = objectAtIndex(colAry, i) Declare Sub removeTableColumn Lib "Cocoa" Selector "removeTableColumn:" (receiver As Ptr, col As Ptr) removeTableColumn(tableViewInst, tCol) next End Sub
- 以下をWindow1にペースト
Protected Sub RemoveAllRow() // 行がなければ戻る if rowMax<=0 then return end if // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // NSIndexSet生成(全行を含む) Dim indexes As Ptr = NSClassFromString("NSMutableIndexSet") // クラスメソッドなので、まずNSMutableIndexSetクラスを取得 Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr Declare Function init Lib "Cocoa" selector "init" (obj_id As Ptr) As Ptr indexes = init(alloc(indexes)) Declare Sub addObject Lib "Cocoa" Selector "addIndex:" (receiver As Ptr, val As Integer) for i As Integer = 0 to rowMax-1 addObject(indexes, i) next // 行を削除 Declare Sub removeRows Lib "Cocoa" Selector "removeRowsAtIndexes:withAnimation:" (receiver As Ptr, idxset As Ptr, anime As Integer) removeRows(tableViewInst, indexes, 0) // clean up Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr) release(indexes) End Sub
- 以下をWindow1にペースト
Protected Sub RemoveCellView() // 列数を取得 Dim colCnt As Integer = GetColCount() // Viewのリリース Dim col, row As Integer for row=0 to rowMax-1 // 行のループ for col=0 to colCnt-1 // 列のループ // 列番号と行番号を指定して、Viewを取得 Declare Function viewAtColumn Lib "Cocoa" Selector "viewAtColumn:row:makeIfNecessary:" (receiver As Ptr, col As Integer, row As Integer, flg As Boolean) As Ptr Dim cellView As Ptr = viewAtColumn(tableviewInst, col, row, true) // clean up Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr) release(cellView) next next End Sub
- 以下をWindow1にペースト
Protected Sub ResetTable() // セルビューのリリース RemoveCellView() // 全行削除 RemoveAllRow() // 行カウンターの初期化 rowMax=0 // 全列削除 RemoveAllCol() // 内部データの初期化 InitDataArray() End Sub
- 以下をWindow1にペースト
Protected Sub SaveFile() Dim f As FolderItem Dim tos As TextOutputStream Dim col, row As Integer Dim st, sep, cnam As String // ファイル保存ダイアログ f=GetSaveFolderItem("","MyInfo.txt") if f=nil then return end if tos=TextOutputStream.Create(f) // ファイルを生成 st="" // ラインの初期化 for col=0 to GetColCount()-1 // 列のループ // 連結の開始時はカンマを追加しない sep="," if col=0 then sep="" st=st+sep+SaveFile2(col,"t").StringValue // ラインに連結 next tos.WriteLine(st) // ラインの書き出し st="" // ラインの初期化 for col=0 to GetColCount()-1 // 列のループ // 連結の開始時はカンマを追加しない sep="," if col=0 then sep="" Dim w As CGFloat = SaveFile2(col,"w") // ラインに連結 st=st+sep+Str(w) next tos.WriteLine(st) // ラインの書き出し st="" // ラインの初期化 for col=0 to GetColCount()-1 // 列のループ // 連結の開始時はカンマを追加しない sep="," if col=0 then sep="" st=st+sep+SaveFile2(col,"k").StringValue // ラインに連結 next tos.WriteLine(st) // ラインの書き出し for row=0 to rowMax-1 // 行のループ st="" // ラインの初期化 for col=0 to GetColCount()-1 // 列のループ // 連結の開始時はカンマを追加しない sep="," if col=0 then sep="" // 列番号と行番号を指定して、Viewを取得 Declare Function viewAtColumn Lib "Cocoa" Selector "viewAtColumn:row:makeIfNecessary:" (receiver As Ptr, col As Integer, row As Integer, flg As Boolean) As Ptr Dim cellView As Ptr = viewAtColumn(tableviewInst, col, row, true) // クラス名を取得 cnam = GetClassName(cellView) select case cnam case "NSTextField" // 文字列 // 文字列を取得 Declare Function stringValue Lib "Cocoa" Selector "stringValue" (receiver As Ptr) As CFStringRef st=st+sep+stringValue(cellView) // ラインに連結 case "NSButton" // チェックボックス // Stateを取得 Declare Function state Lib "Cocoa" Selector "state" (receiver As Ptr) As Integer st=st+sep+Str(state(cellView)) // ラインに連結 case "NSPopUpButton" // ポップアップメニュー // ListIndexを取得 Declare Function indexOfSelectedItem Lib "Cocoa" Selector "indexOfSelectedItem" (receiver As Ptr) As Integer st=st+sep+Str(indexOfSelectedItem(cellView)) // ラインに連結 case "NSSegmentedControl" // セグメンテッドコントロール // Segmentを取得 Declare Function selectedSegment Lib "Cocoa" Selector "selectedSegment" (receiver As Ptr) As Integer st=st+sep+Str(selectedSegment(cellView)) // ラインに連結 case "NSSlider" // スライダー // スライダー値を取得 Declare Function doubleValue Lib "Cocoa" Selector "doubleValue" (receiver As Ptr) As Double st=st+sep+Str(doubleValue(cellView)) // ラインに連結 case "NSLevelIndicator" // レベルインジケーター // インジケーター値を取得 Declare Function doubleValue Lib "Cocoa" Selector "doubleValue" (receiver As Ptr) As Double st=st+sep+Str(doubleValue(cellView)) // ラインに連結 end select next tos.WriteLine(st) // ラインの書き出し next // ストリームのクローズ tos.Close End Sub
- 以下をWindow1にペースト
Protected Function SaveFile2(idx As Integer, param As String) as Variant // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // データ領域の生成 Dim dataArray As Ptr dataArray = NSClassFromString("NSMutableArray") // クラスメソッドなので、まずNSMutableArrayクラスを取得 Declare Function getArray Lib "Cocoa" Selector "array" (receiver As Ptr) As Ptr // Return Array* dataArray = getArray(dataArray) // 列集合を取得 Declare Function tableColumns Lib "Cocoa" Selector "tableColumns" (receiver As Ptr) As Ptr Dim colAry As Ptr = tableColumns(tableviewInst) // 指定された列を取得 Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr Dim column As Ptr = objectAtIndex(colAry, idx) // 指定された種別のパラメータを返す select case param case "t" Declare Function title Lib "Cocoa" Selector "title" (receiver As Ptr) As CFStringRef Dim s As String = title(column) // タイトルを取得 return s case "w" Declare Function width Lib "Cocoa" Selector "width" (receiver As Ptr) As CGFloat Dim w As CGFloat = width(column) // 列幅を取得 return w case "k" Declare Function identifier Lib "Cocoa" selector "identifier" (class_id As Ptr) As CFStringRef Dim ident As String = identifier(column) // 列アイデンティファイア Dim s As String = NthField(ident,":",1) // 列種別を取得 return s end select End Function
- 以下をWindow1にペースト
Protected Sub SetSortedData(dataArray As Ptr) // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // 列集合を取得 Declare Function tableColumns Lib "Cocoa" Selector "tableColumns" (receiver As Ptr) As Ptr Dim colAry As Ptr = tableColumns(tableviewInst) // 列数を取得 Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer Dim colCnt As Integer = count(colAry) // Viewのループ Dim col, row As Integer Dim st, cnam As String for row=0 to rowMax-1 // 行のループ for col=0 to colCnt-1 // 列のループ // 列を順に取得 Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr Dim column As Ptr = objectAtIndex(colAry, col) // 列アイデンティファイア Declare Function identifier Lib "Cocoa" selector "identifier" (class_id As Ptr) As CFStringRef Dim ident As String = identifier(column) // 順に行データを取得 Dim objct As Ptr = objectAtIndex(dataArray, row) // 列アイデンティファイアが一致するデータを取得 Declare Function objectForKey Lib "Cocoa" Selector "objectForKey:" (receiver As Ptr, key As CFStringRef) As CFStringRef st = objectForKey(objct, ident) // 列番号と行番号を指定して、Viewを取得 Declare Function viewAtColumn Lib "Cocoa" Selector "viewAtColumn:row:makeIfNecessary:" (receiver As Ptr, col As Integer, row As Integer, flg As Boolean) As Ptr Dim cellView As Ptr = viewAtColumn(tableviewInst, col, row, true) // クラス名を取得 cnam=GetClassName(cellView) // 値をセット select case cnam case "NSTextField" // 文字列 Declare Sub setStringValue Lib "Cocoa" Selector "setStringValue:" (receiver As Ptr, val As CFStringRef) setStringValue(cellView, st) case "NSButton" // チェックボックス Declare Sub setState Lib "Cocoa" Selector "setState:" (receiver As Ptr, val As Integer) setState(cellView, Val(st)) case "NSPopUpButton" // ポップアップメニュー Declare Sub selectItemAtIndex Lib "Cocoa" Selector "selectItemAtIndex:" (receiver As Ptr, val As Integer) selectItemAtIndex(cellView, Val(st)) case "NSSegmentedControl" // セグメンテッドコントロール Declare Sub setSelectedSegment Lib "Cocoa" Selector "setSelectedSegment:" (receiver As Ptr, val As Integer) setSelectedSegment(cellView, Val(st)) case "NSSlider" // スライダー Declare Sub setDoubleValue Lib "Cocoa" Selector "setDoubleValue:" (receiver As Ptr, val As Double) setDoubleValue(cellView, Val(st)) case "NSLevelIndicator" // レベルインジケーター Declare Sub setDoubleValue Lib "Cocoa" Selector "setDoubleValue:" (receiver As Ptr, val As Double) setDoubleValue(cellView, Val(st)) end select next next End Sub
- 以下をWindow1にペースト
注)当初は単純に代入していたが、oldDescriptorsはここを過ぎると解放されてしまうようで、参照が不安定になるため、コピーするようにした。(2021.04.20)Protected Sub sortDescriptors(id as Ptr, sel as CString, tableView As Ptr, oldDescriptors As Ptr) // 過去のデスクリプタを保持 Declare Function mutableCopy Lib "Cocoa" Selector "mutableCopy" (receiver As Ptr) As Ptr oldDescs = mutableCopy(oldDescriptors) End Sub
- 以下をWindow1にペースト
Protected Function textEndEdit(id as Ptr, sel as CString, control As Ptr, fEditor As Ptr) as Boolean // 編集後の値を内部データに反映させる actionEventValue(id,control) return true End Function
- 以下をWindow1にペースト
Protected Function viewForTable(id As Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr, rowidx As Integer) as Ptr // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr // 列のアイデンティファイアを取得 Declare Function identifier Lib "Cocoa" Selector "identifier" (receiver As Ptr) As CFStringRef Dim ident As String = identifier(tableColumn) // アイデンティファイアから列種別と固有化番号を取得 Dim kind As String = NthField(ident,":",1) // 行データを取得 Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr Dim objct As Ptr = objectAtIndex(dataArrayNS, rowidx) // 列アイデンティファイアが一致するデータを取得 Declare Function objectForKey Lib "Cocoa" Selector "objectForKey:" (receiver As Ptr, key As CFStringRef) As CFStringRef Dim st As String = objectForKey(objct, ident) Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr Declare Function init Lib "Cocoa" selector "init" (obj_id As Ptr) As Ptr // 列種別ごとに処理 Dim pnt1 As Ptr select case kind case "text" // 文字列 // インスタンス生成 pnt1 = NSClassFromString("NSTextField") // クラスメソッドなので、まずNSTextFieldクラスを取得 pnt1 = init(alloc(pnt1)) Declare Sub setDelegate Lib "Cocoa" Selector "setDelegate:" (receiver As Ptr, obj As Ptr) setDelegate(pnt1, tableviewInst) // パラメーター declare sub setBordered lib "Cocoa" Selector "setBordered:" (obj as ptr, value as Boolean) setBordered(pnt1, false) Dim clr As Ptr = NSClassFromString("NSColor") declare function clearColor lib "Cocoa" Selector "clearColor" (obj as ptr) as ptr declare sub setBackgroundColor lib "Cocoa" Selector "setBackgroundColor:" (obj as ptr, col as ptr) setBackgroundColor(pnt1, clearColor(clr)) declare function isEditable lib "Cocoa" Selector "isEditable" (id as ptr) as Boolean declare sub setEditable lib "Cocoa" Selector "setEditable:" (id as ptr, value as Boolean) setEditable(pnt1, isEditable(tableColumn)) declare function myCell lib "Cocoa" Selector "cell" (id as ptr) as ptr declare sub setLineBreakMode lib "Cocoa" Selector "setLineBreakMode:" (id as ptr, mode as integer) setLineBreakMode(myCell(pnt1), 4) // 4 = NSLineBreakByTruncatingTail // データ Declare Sub setStringValue Lib "Cocoa" Selector "setStringValue:" (receiver As Ptr, val As CFStringRef) setStringValue(pnt1, st) case "chkb" // チェックボックス // インスタンス生成 pnt1 = NSClassFromString("NSButton") // クラスメソッドなので、まずNSButtonクラスを取得 // macOS 10.0–10.14 pnt1 = init(alloc(pnt1)) Declare Sub setButtonType Lib "Cocoa" Selector "setButtonType:" (receiver As Ptr, val As UInteger) setButtonType(pnt1, 3) // 3 = NSSwitchButton(チェックボックス) Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr) setTarget(pnt1, tableviewInst) // Targetを設定 Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr) setAction(pnt1, NSSelectorFromString("action:")) // Actionを設定 // macOS 10.12+(こちらを用いる場合は、RemoveCellView()でreleaseしないよう、処理を追加のこと) 'Declare Function checkboxWithTitle Lib "Cocoa" Selector "checkboxWithTitle:target:action:" (receiver As Ptr, title As CFStringRef, tgt As Ptr, act As Ptr) As Ptr 'pnt1 = checkboxWithTitle(pnt1, "", tableviewInst, NSSelectorFromString("action:")) // ここでTarget,Actionも設定 // パラメーター Declare Sub setAllowsMixedState Lib "Cocoa" Selector "setAllowsMixedState:" (receiver As Ptr, val As Boolean) setAllowsMixedState(pnt1, true) // データ Declare Sub setState Lib "Cocoa" Selector "setState:" (receiver As Ptr, val As Integer) setState(pnt1, Val(st)) case "popu" // ポップアップメニュー // インスタンス生成 pnt1 = NSClassFromString("NSPopUpButton") // クラスメソッドなので、まずNSPopUpButtonクラスを取得 pnt1 = init(alloc(pnt1)) // パラメーター Declare Sub bezelStyle Lib "Cocoa" Selector "setBezelStyle:" (receiver As Ptr, val As Integer) bezelStyle(pnt1, 11) // NSBezelStyleTexturedRounded Declare Sub addItemsWithTitles Lib "Cocoa" selector "addItemsWithTitles:" (class_id As Ptr, menu As Ptr) addItemsWithTitles(pnt1, makeMenuArray()) // メニューを生成してセット Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr) setTarget(pnt1, tableviewInst) // Targetを設定 Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr) setAction(pnt1, NSSelectorFromString("action:")) // Actionを設定 // データ Declare Sub selectItemAtIndex Lib "Cocoa" Selector "selectItemAtIndex:" (receiver As Ptr, val As Integer) selectItemAtIndex(pnt1, Val(st)) case "segm" // セグメンテッドコントロール // インスタンス生成 pnt1 = NSClassFromString("NSSegmentedControl") // クラスメソッドなので、まずNSSegmentedControlクラスを取得 pnt1 = init(alloc(pnt1)) Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr) setTarget(pnt1, tableviewInst) // Targetを設定 Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr) setAction(pnt1, NSSelectorFromString("action:")) // Actionを設定 // パラメーター Declare Sub setSegmentStyle Lib "Cocoa" Selector "setSegmentStyle:" (receiver As Ptr, val As Integer) setSegmentStyle(pnt1, 3) // NSSegmentStyleRoundRect Declare Sub setSegmentCount Lib "Cocoa" Selector "setSegmentCount:" (receiver As Ptr, cnt As Integer) setSegmentCount(pnt1, 3) // セグメント数 Declare Function width Lib "Cocoa" Selector "width" (receiver As Ptr) As CGFloat Dim w As CGFloat = width(tableColumn) // 列幅を取得 Declare Sub setWidth Lib "Cocoa" Selector "setWidth:forSegment:" (receiver As Ptr, w As CGFloat, no As Integer) setWidth(pnt1, (w-7)/3, 0) // セグメントの幅を列幅の1/3に setWidth(pnt1, (w-7)/3, 1) setWidth(pnt1, (w-7)/3, 2) Declare Sub setLabel Lib "Cocoa" Selector "setLabel:forSegment:" (receiver As Ptr, w As CFStringRef, no As Integer) setLabel(pnt1, "1", 0) setLabel(pnt1, "2", 1) setLabel(pnt1, "3", 2) // データ Declare Sub setSelectedSegment Lib "Cocoa" Selector "setSelectedSegment:" (receiver As Ptr, val As Integer) setSelectedSegment(pnt1, Val(st)) case "slid" // スライダー // インスタンス生成 pnt1 = NSClassFromString("NSSlider") // クラスメソッドなので、まずNSSliderクラスを取得 pnt1 = init(alloc(pnt1)) Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr) setTarget(pnt1, tableviewInst) // Targetを設定 Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr) setAction(pnt1, NSSelectorFromString("action:")) // Actionを設定 // パラメーター Declare Sub setMinValue Lib "Cocoa" Selector "setMinValue:" (receiver As Ptr, val As Double) setMinValue(pnt1, 0.0) Declare Sub setMaxValue Lib "Cocoa" Selector "setMaxValue:" (receiver As Ptr, val As Double) setMaxValue(pnt1, 10.0) // データ Declare Sub setDoubleValue Lib "Cocoa" Selector "setDoubleValue:" (receiver As Ptr, val As Double) setDoubleValue(pnt1, Val(st)) case "indi" // レベルインジケーター // インスタンス生成 pnt1 = NSClassFromString("NSLevelIndicator") // クラスメソッドなので、まずNSLevelIndicatorクラスを取得 pnt1 = init(alloc(pnt1)) // パラメーター Declare Sub setMinValue Lib "Cocoa" Selector "setMinValue:" (receiver As Ptr, val As Double) setMinValue(pnt1, 0.0) Declare Sub setMaxValue Lib "Cocoa" Selector "setMaxValue:" (receiver As Ptr, val As Double) setMaxValue(pnt1, 10.0) Declare Sub setWarningValue Lib "Cocoa" Selector "setWarningValue:" (receiver As Ptr, val As Double) setWarningValue(pnt1, 5.0) Declare Sub setCriticalValue Lib "Cocoa" Selector "setCriticalValue:" (receiver As Ptr, val As Double) setCriticalValue(pnt1, 9.0) // データ Declare Sub setDoubleValue Lib "Cocoa" Selector "setDoubleValue:" (receiver As Ptr, val As Double) setDoubleValue(pnt1, Val(st)) end select // オブジェクトを返す return pnt1 End Function
- 以下をWindow1にペースト(できなければNotesに、メモ名:File Spec.としてFile Spec.以降をコピー)
File Spec. <入出力ファイルの仕様> ・1〜3行目は列情報、4行目以降がデータ ・列の区切り記号はカンマ(エスケープ処理はしていないので、カンマをテキストデータに含めることはできない) ・列情報 1行目:列名 2行目:列幅(実数扱い) 3行目:列種別(テキスト=text、チェックボックス=chkb、ポップアップメニュー=popu、セグメンテッドコントロール=segm、スライダー=slid、レベルインジケーター=indi) ・記述例 col 1,col 2,col 3 150,150,150 text,indi,popu 0001,0,0 … ・注意事項 ・エラーチェックはしていないので、手作業で作成する場合は慎重に
- 以下をWindow1にペースト(できなければプロパティに、名前:dataArrayNS、データ型:Ptr、を追加)
Protected Property dataArrayNS as Ptr
- 以下をWindow1にペースト(できなければプロパティに、名前:oldDescs、データ型:Ptr、を追加)
Protected Property oldDescs as Ptr
- 以下をWindow1にペースト(できなければプロパティに、名前:previousCol、データ型:String、を追加)
Protected Property previousCol as String
- 以下をWindow1にペースト(できなければプロパティに、名前:rowMax、データ型:Integer、を追加)
Protected Property rowMax as Integer
- 以下をWindow1にペースト(できなければプロパティに、名前:tableviewInst、データ型:Ptr、を追加)
Protected Property tableviewInst as Ptr
- 新規クラス(名前は、ここでは「NSViewListBox」)を作成。
- 以下をNSViewListBoxにペースト(できなければ移譲に、デリゲート名:ActionDelegate、引数:id As Ptr, sel as CString, sender As Ptr、戻り値型:Ptr、を追加)
Private Sub ActionDelegate(id As Ptr, sel as CString, sender As Ptr)
- 以下をNSViewListBoxにペースト(できなければ移譲に、デリゲート名:ActionDelegate1、引数:id As Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr, rowidx As Integer、戻り値型:Ptr、を追加)
Private Function ActionDelegate1(id As Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr, rowidx As Integer) as Ptr
- 以下をNSViewListBoxにペースト(できなければ移譲に、デリゲート名:ActionDelegate2、引数:id as Ptr, sel as CString, tableView As Ptr, oldDescriptors As Ptr、を追加)
Private Sub ActionDelegate2(id as Ptr, sel as CString, tableView As Ptr, oldDescriptors As Ptr)
- 以下をNSViewListBoxにペースト(できなければ移譲に、デリゲート名:ActionDelegate3、引数:id as Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr、を追加)
Private Sub ActionDelegate3(id as Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr)
- 以下をNSViewListBoxにペースト(できなければ移譲に、デリゲート名:ActionDelegateT、引数:id As Ptr, sel as CString, control As Ptr, fEditor As Ptr、戻り値型:Boolean、を追加)
Private Function ActionDelegateT(id as Ptr, sel as CString, control As Ptr, fEditor As Ptr) as Boolean
- 以下をNSViewListBoxにペースト
Public Sub Constructor(byRef inst As Ptr, win As Window, rect As NSRect, action As ActionDelegate, action1 As ActionDelegate1, action2 As ActionDelegate2, action3 As ActionDelegate3, actionT As ActionDelegateT) rect.y = win.Height - rect.y - rect.h // y座標系が、CocoaとXojoで反転しているので、補正 // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr Declare Function initWithFrame Lib "Cocoa" selector "initWithFrame:" (obj_id As Ptr, frame As NSRect) As Ptr // NSTableViewを継承したカスタムクラスを作成。初回のみ makeClass() // NSTableViewのインスタンス作成 Dim tableView As Ptr = initWithFrame(alloc(NSTableViewClass), NSMakeRect(0, 0, 0, 0)) // rectを指定すると、スクロール時に描画が乱れる(ヘッダーずれ他) Declare Sub setFocusRingType Lib "Cocoa" Selector "setFocusRingType:" (receiver As Ptr, typ As Integer) setFocusRingType(tableView, 1) // 1 = NSFocusRingTypeNone(フォーカスリングなし) Declare Sub setUsesAlternatingRow Lib "Cocoa" Selector "setUsesAlternatingRowBackgroundColors:" (receiver As Ptr, flg As Boolean) setUsesAlternatingRow(tableView, true) // 1行ごとに背景色を変える Declare Sub setRowHeight Lib "Cocoa" Selector "setRowHeight:" (receiver As Ptr, typ As CGFloat) setRowHeight(tableView, 22.0) // 行の高さ Declare Sub setSelectionHighlightStyle Lib "Cocoa" Selector "setSelectionHighlightStyle:" (receiver As Ptr, typ As Integer) setSelectionHighlightStyle(tableView, -1) // -1 = NSTableViewSelectionHighlightStyleNone(行のハイライト表示を行わない) Declare Sub setColumnAutoresizingStyle Lib "Cocoa" Selector "setColumnAutoresizingStyle:" (receiver As Ptr, typ As Integer) setColumnAutoresizingStyle(tableView, 0) // 0 = NSTableViewNoColumnAutoresizing(列幅の自動リサイズを行わない) // NSScrollViewのインスタンス生成 Dim scrollView As Ptr = NSClassFromString("NSScrollView") // クラスメソッドなので、まずNSScrollViewクラスを取得 scrollView = initWithFrame(alloc(scrollView), rect) Declare Sub setBorderType Lib "Cocoa" Selector "setBorderType:" (receiver As Ptr, typ As Integer) setBorderType(scrollView, 2) // 2 = NSBezelBorder Declare Sub setHasVerticalScroller Lib "Cocoa" Selector "setHasVerticalScroller:" (receiver As Ptr, typ As Boolean) setHasVerticalScroller(scrollView, true) Declare Sub setHasHorizontalScroller Lib "Cocoa" Selector "setHasHorizontalScroller:" (receiver As Ptr, typ As Boolean) setHasHorizontalScroller(scrollView, true) Declare Sub setAutohidesScrollers Lib "Cocoa" Selector "setAutohidesScrollers:" (receiver As Ptr, typ As Boolean) setAutohidesScrollers(scrollView, true) Declare Sub setAutoresizingMask Lib "Cocoa" Selector "setAutoresizingMask:" (receiver As Ptr, typ As Integer) setAutoresizingMask(scrollView, 2+16) // 2+16 = NSViewWidthSizable | NSViewHeightSizable // DataSource, Delegateの設定 Declare Sub setDataSource Lib "Cocoa" Selector "setDataSource:" (receiver As Ptr, obj As Ptr) setDataSource(tableView, tableView) Declare Sub setDelegate Lib "Cocoa" Selector "setDelegate:" (receiver As Ptr, obj As Ptr) setDelegate(tableView, tableView) // スクロールビューにテーブルビューをセット Declare Sub setDocumentView Lib "Cocoa" Selector "setDocumentView:" (receiver As Ptr, obj As Ptr) setDocumentView(scrollView, tableView) // ウィンドウのビューを取得 Declare Function contentView Lib "Cocoa" selector "contentView" (class_id As Integer) As Ptr Dim pnt2 As Ptr = contentView(win.handle) // スクロールビューをウィンドウのビューに追加 Declare Sub addSubview Lib "Cocoa" selector "addSubview:" (class_id As Ptr, view As Ptr) addSubview(pnt2, scrollView) // テーブルビューのインスタンスを保持 InstancePtr = tableView inst = InstancePtr // Window側でActionを受け取るメソッドを登録 ActionHandler = action ActionHandler1 = action1 ActionHandler2 = action2 ActionHandler3 = action3 ActionHandlerT = actionT End Sub
- 以下をNSViewListBoxにペースト(できなければプロパティに、名前:InstancePtr、データ型:Ptr、を追加)
Private Property InstancePtr as Ptr
- 以下をNSViewListBoxにペースト
Private Shared Sub makeClass() // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr // Declare宣言 Declare Function objc_allocateClassPair Lib "Cocoa" (superclass As Ptr, name As CString, extraBytes As Integer) as Ptr Declare Sub objc_registerClassPair Lib "Cocoa" (cls As Ptr) Declare Function class_addMethod Lib "Cocoa" (cls As Ptr, name As Ptr, imp As Ptr, types As CString) As Boolean // 既にクラス作成済なら戻る if NSTableViewClass <> nil then return end if // クラス名をmyNSTableView、メタクラス名をNSTableViewにして、生成 Dim newClassId As Ptr = objc_allocateClassPair(NSClassFromString("NSTableView"), "myNSTableView", 0) // ランタイムに登録(参照を可能とするため) objc_registerClassPair newClassId // DataSourceの対象となるメソッドを追加(numberOfRowsInTableView:をXojo側で用意したmyNumberOfRowsメソッドで受け取る。) if not class_addMethod (newClassId, NSSelectorFromString("numberOfRowsInTableView:"), AddressOf myNumberOfRows, "i12@0:4@8") then msgBox "error1." return end if // DataSourceの対象となるメソッドを追加(tableView:sortDescriptorsDidChange:をXojo側で用意したmySortDescriptorsメソッドで受け取る。) if not class_addMethod (newClassId, NSSelectorFromString("tableView:sortDescriptorsDidChange:"), AddressOf mySortDescriptors, "v16@0:4@8@12") then msgBox "error2." return end if // Delegateの対象となるメソッドを追加(tableView:didClickTableColumn:をXojo側で用意したmyDidClickTableColumnメソッドで受け取る。) if not class_addMethod (newClassId, NSSelectorFromString("tableView:didClickTableColumn:"), AddressOf myDidClickTableColumn, "v16@0:4@8@12") then msgBox "error3." return end if // Delegateの対象となるメソッドを追加(tableView:viewForTableColumn:row:をXojo側で用意したmyViewForTableメソッドで受け取る。) if not class_addMethod (newClassId, NSSelectorFromString("tableView:viewForTableColumn:row:"), AddressOf myViewForTable, "@20@0:4@8@12i16") then msgBox "error4." return end if // Delegateの対象となるメソッドを追加(control:textShouldEndEditing:をXojo側で用意したmyEditTableメソッドで受け取る。) if not class_addMethod (newClassId, NSSelectorFromString("control:textShouldEndEditing:"), AddressOf myTextEndEdit, "@24@0:8@16") then msgBox "error5." return end if // Tarrgetに送られてきたActionの受け口となるメソッドを追加(action:をXojo側で用意したactionEventメソッドで受け取る。) if not class_addMethod (newClassId, NSSelectorFromString("action:"), AddressOf myActionEvent, "@@:@") then msgBox "error6." return end if // クラスを保持 NSTableViewClass = newClassId End Sub
- 以下をNSViewListBoxにペースト
Private Shared Sub myActionEvent(id As Ptr, SEL As CString, sender As Ptr) // Constructorで登録した、Actionを受け取るメソッドを呼び出す ActionHandler.Invoke(id, sel, sender) End Sub
- 以下をNSViewListBoxにペースト
Private Shared Sub myDidClickTableColumn(id as Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr) // Constructorで登録した、Actionを受け取るメソッドを呼び出す ActionHandler3.Invoke(id, sel, tableView, tableColumn) End Sub
- 以下をNSViewListBoxにペースト
Private Shared Function myNumberOfRows(id as Ptr, sel as CString, tableView As Ptr) as Integer // 行数を返す(常に0行) return 0 End Function
- 以下をNSViewListBoxにペースト
Private Shared Sub mySortDescriptors(id as Ptr, sel as CString, tableView As Ptr, oldDescriptors As Ptr) // Constructorで登録した、Actionを受け取るメソッドを呼び出す ActionHandler2.Invoke(id, sel, tableView, oldDescriptors) End Sub
- 以下をNSViewListBoxにペースト
Private Shared Function myTextEndEdit(id as Ptr, sel as CString, control As Ptr, fEditor As Ptr) as Boolean // Constructorで登録した、Actionを受け取るメソッドを呼び出す return ActionHandlerT.Invoke(id, sel, control, fEditor) End Function
- 以下をNSViewListBoxにペースト
Private Shared Function myViewForTable(id as Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr, rowidx As Integer) as Ptr // Constructorで登録した、Actionを受け取るメソッドを呼び出す return ActionHandler1.Invoke(id, sel, tableView, tableColumn, rowidx) End Function
- 以下をNSViewListBoxにペースト(できなければ共有プロパティに、名前:ActionHandler、データ型:ActionDelegate、を追加)
Private Shared Property ActionHandler as ActionDelegate
- 以下をNSViewListBoxにペースト(できなければ共有プロパティに、名前:ActionHandler1、データ型:ActionDelegate1、を追加)
Private Shared Property ActionHandler1 as ActionDelegate1
- 以下をNSViewListBoxにペースト(できなければ共有プロパティに、名前:ActionHandler2、データ型:ActionDelegate2、を追加)
Private Shared Property ActionHandler2 as ActionDelegate2
- 以下をNSViewListBoxにペースト(できなければ共有プロパティに、名前:ActionHandler3、データ型:ActionDelegate3、を追加)
Private Shared Property ActionHandler3 as ActionDelegate3
- 以下をNSViewListBoxにペースト(できなければ共有プロパティに、名前:ActionHandlerT、データ型:ActionDelegateT、を追加)
Private Shared Property ActionHandlerT as ActionDelegateT
- 以下をNSViewListBoxにペースト(できなければ共有プロパティに、名前:NSTableViewClass、データ型:Ptr、を追加)
Private Shared Property NSTableViewClass as Ptr
- 他に、NSMakeRect(メソッド)、NSRect(構造体)が必要ですが、macoslibからコピーさせて頂きました。(上記NSViewListBoxまたは別途モジュールを用意してコピーする。)
注)macoslibではNSRectのメンバーの型にSingleが割り当てられているが、64bitにも対応したい場合は、CGFloatに書き換える。
おわりに
列の入れ替えは、プロパティで指定しているだけですが、入れ替え結果は直ちに列の並び順にも反映される等、こちらは何もしなくていいのは助かります。
セグメンテッドコントロールは(以前のコラムでもそんな印象を持ちましたが)他と違うようで、他は幅を省略するとセル幅に追随するのですが、明示的にセットする必要がありました。(何か設定があるのでしょうか?)
あと、テキストだけだと、どこまで行があるのかが分かかりずらいので、セル枠を描くなどした方がいいかもしれません。
本格的に利用しようとすると、DataSource/Delegateメソッドの更なる追加や、Key-Value CodingやCocoa Bindingへの理解を深める等の必要もありそうで、結構大変そうです。
お世話になったサイト
貴重な情報をご提供頂いている皆様に、お礼申し上げます。(以下、順不同)
参考サイト(1):NSCellを使ってはいけない - Qiita
参考サイト(2):NSTableView - src256 wiki
参考サイト(3):NSTableView (Creation and Loading from Code) – Knowledge Stack
更新履歴
2021.04.20 Xojoでの実装の26項を改訂
2019.07.11 改訂版・新規作成
[Home] [MacSoft] [Donation] [History] [Privacy Policy] [Affiliate Policy]