ホームページ>開発ツール>Xojo / Real Studio Trial and Error・CocoaのDeclareでツールバーを実装する・メニューに対応する
Xojo / Real Studio Trial and Error
目次
CocoaのDeclareでツールバーを実装する・メニューに対応する
はじめに
以下は、Xojo Cocoaビルドについての話題です。
NSToolbarのメニュー対応について、試してみました。
なお検証には、Xojo 2017 Release 1.1を用いています。(Mac mini 2018 + macOS 10.14.6 Mojave)
方針
NSToolbarでは、ツールバーのモードが「テキストのみ」の場合と、ツールバーの横幅が狭くてアイテムが表示できない時のために、メニューが用意されています。
「テキストのみ」モードでは、アイテムのラベルしか表示されないため、例えばセグメンテッドコントロールは、現在選択されている要素を示したり変更の指示をするために、メニューが使われます。
ツールバーの横幅が狭くてアイテムが表示できない時のメニューは、オーバーフローメニューと呼ばれ、山かっこ(と、公式ページにはあった)が表示されているか否かで利用可能であるかが判ります。
ただし、XojoでNSToolbar(のインスタンス)を表示した場合は、初期状態では表示されません。
ツールバーのモードを変えると表示されることから、例によって、何らかの齟齬が発生しているものと思われます。
なので、ウィンドウがアクティブになった辺りで、モードを別のものに変えてすぐに戻す処理を加えておくことで、表示されるようになります。
次はメニュー本体ですが、オーバーフローメニューでしか使わないツールバーアイテムは、自身が既に持っているメニューをmenuFormRepresentationで呼び出し、必要な情報を追加してやることで対応できます。
そうでない場合は、独自にメニュー作成後、付け替えます。
(注:テキストのみモードでは、メニュー付ツールバーアイテムのメニューも、アイコン用ではなく、こちらが使われます。)
参考サイト(1):Setting a Toolbar Item’s Representation
(前後のページにも有用な情報があるので、一通り目を通しておいたほうがいいと思われます。)
メニューは付加しただけでは有効になりませんが、ターゲット/アクションを設定することで有効になります。この辺は、通常のメニューと同じです。
また、メニューの有効/無効(または常に無効)は、これも通常のメニュー同様、validateMenuItem:の戻り値で指定します。
メニューのアクションは、今回はツールバーアイテムのアクションと同じメソッドを割り付け、タグで判別することとします。
なお、サーチフィールドは、メニュー側に標準的な機能はないようで、例えばFinderはウィンドウ内に検索バーを表示、メール.appはウィンドウ幅を拡大して表示等、個別に対応している?ようです。
このようなこともあって、今回はサーチフィールドについては常に無効とします。
それと、これは実装してから判明したことですが、アクションに渡ってくる送出元(Sender)がサブメニューの場合、そこから(元の)メニューを辿る方法が分からなかったため、タグ番号からツールバーアイテムを取得してそこからメニューを取得するという、ややまどろっこしい方法をとりました。この辺は要調査ではあります。
残りの(主な)方針は、以下の通りとしました。
- 前回プロジェクトをベースとする(ただし扱いとしては新規とし、改変のないものは前回プロジェクトからコピーする。)
- メニューのサブメニューは1階層までとする。(複数階層化するには再帰呼び出しが有効になるが、今回はそこまではしない。)
- ツールバーはクラス化する。ただし、個別にカスタマイズするベースという位置付けで、汎用クラスにすることまでは考えない。
Xojoでの実装
【ソースコードのコピー&ペーストについて】
・ソースコード(グレー背景部分の全文)をコピーし、指定のオブジェクトにペーストすると、(新規作成して名前等を個別にコピー&ペーストしなくても)復元されます。
・ペーストはオブジェクトに行って下さい。オブジェクト内のEvent Handlers/Methods/Properties等にペーストしても、うまくいかない場合があります。
実行してみたところ、付加したメニューが機能することを確認しました。
- Xojoで新規プロジェクトを作成
- Window1にListBox(名前はListBox1)、PushButton2個(名前はPushButton1とPushButton2)を追加(注:動作確認用であり、必須ではない。)
- 以下をPushButton1にペースト
Sub Action() Handles Action SetButtonEnable(601,not GetButtonEnable(601)) End Sub
- 以下をPushButton2にペースト
Sub Action() Handles Action EnableNewWin = not EnableNewWin End Sub
- 以下をWindow1にペースト
Sub Activate() Handles Activate // ツールバー縮小時のメニュー表示がなされないことの対策(モードを切り替えると(再描画が発生して?)表示される) ToolbarDispMode() End Sub
- 以下をWindow1にペースト
Sub Open() Handles Open // ツールバー生成 Dim t As NSToolbar = new NSToolbar(self.Handle, AddressOf ToolbarItemClicked, AddressOf ToolbarValidateMenuItem) End Sub
- 以下をWindow1にペースト
Protected Function GetButtonEnable(tagNo As Integer) as Boolean // 指定されたタグ番号を持つアイテムを取得 Dim pnt As Ptr = GetToolItem(tagNo) // 現在の有効/無効値を取得して返す Declare Function isEnabled Lib "Cocoa" Selector "isEnabled" (receiver As Ptr) As Boolean return isEnabled(pnt) End Function
- 以下をWindow1にペースト
Protected Function GetToolItem(tagNo As Integer) as Ptr // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSStringFromClass Lib "Cocoa" (aClass As Ptr) As CFStringRef Declare Function myClass Lib "Cocoa" Selector "class" (receiver As Ptr) As Ptr Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, info As Integer) As Ptr Declare Function view Lib "Cocoa" Selector "view" (receiver As Ptr) As Ptr Declare Function tagForSegment Lib "Cocoa" Selector "tagForSegment:" (receiver As Ptr, seg As Integer) As Integer Declare Function cell Lib "Cocoa" Selector "cell" (receiver As Ptr) As Ptr // ツールバーの取得 declare function toolbar lib "Cocoa" selector "toolbar" (obj_id as Integer) as Ptr Dim pnt1 As Ptr = toolbar(Window1.Handle) // ツールバーアイテム(配列)の取得 declare function items lib "Cocoa" selector "items" (receiver As Ptr) as Ptr Dim pnt2 As Ptr = items(pnt1) // 配列の個数の取得 declare function count lib "Cocoa" selector "count" (receiver As Ptr) as Integer Dim cnt As Integer = count(pnt2) Dim i, tag As Integer Dim pnt3, pnt4, cls1 As Ptr for i=0 to cnt-1 // ツールバーアイテムを順番に取得 pnt3 = objectAtIndex(pnt2, i) // viewの取得(コントロール類はviewで取得できる) pnt4 = view(pnt3) // コントロールのクラス名を文字列で取得 cls1 = myClass(pnt4) if NSStringFromClass(cls1)="NSSegmentedControl" then // セグメンテッドコントロール tag = tagForSegment(cell(pnt4),0) if tag=tagNo then // 指定されたタグなら return pnt3 // ツールバーアイテムを返す end if end if next End Function
- 以下をWindow1にペースト
Protected Function GetToolItemSearch() as Ptr // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSStringFromClass Lib "Cocoa" (aClass As Ptr) As CFStringRef Declare Function myClass Lib "Cocoa" Selector "class" (receiver As Ptr) As Ptr Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, info As Integer) As Ptr Declare Function view Lib "Cocoa" Selector "view" (receiver As Ptr) As Ptr Declare Function cell Lib "Cocoa" Selector "cell" (receiver As Ptr) As Ptr // ツールバーの取得 declare function toolbar lib "Cocoa" selector "toolbar" (obj_id as Integer) as Ptr Dim pnt1 As Ptr = toolbar(Window1.Handle) // ツールバーアイテム(配列)の取得 declare function visibleItems lib "Cocoa" selector "visibleItems" (receiver As Ptr) as Ptr Dim pnt2 As Ptr = visibleItems(pnt1) // 配列の個数の取得 declare function count lib "Cocoa" selector "count" (receiver As Ptr) as Integer Dim cnt As Integer = count(pnt2) Dim i As Integer Dim pnt3, pnt4, cls1 As Ptr for i=0 to cnt-1 // ツールバーアイテムを順番に取得 pnt3 = objectAtIndex(pnt2, i) // viewの取得(コントロール類はviewで取得できる) pnt4 = view(pnt3) // コントロールのクラス名を文字列で取得 cls1 = myClass(pnt4) if NSStringFromClass(cls1)="NSSearchField" then // サーチフィールドなら return cell(pnt4) // サーチフィールドのセルを返す end if next End Function
- 以下をWindow1にペースト
Protected Sub SetButtonEnable(tagNo As Integer, flg As Boolean) // 指定されたタグ番号を持つアイテムを取得 Dim pnt As Ptr = GetToolItem(tagNo) // 値をセット Declare Sub setEnabled Lib "Cocoa" Selector "setEnabled:" (receiver As Ptr, selected As Boolean) setEnabled(pnt, flg) End Sub
- 以下をWindow1にペースト
Protected Sub SetSegmentState(tag0 As Integer, menuitem As Ptr, idx As Integer) Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr Declare Sub setState Lib "Cocoa" Selector "setState:" (receiver As Ptr, flg As Integer) Declare Sub setSelected Lib "Cocoa" Selector "setSelected:forSegment:" (receiver As Ptr, selected As Boolean, no As Integer) // ツールバーアイテムの取得 Dim segment0 As Ptr = GetToolItem(tag0) // セグメンテッドコントロールの取得 Declare Function myView Lib "Cocoa" Selector "view" (receiver As Ptr) As Ptr Dim segment1 As Ptr = myView(segment0) // ツールバーアイテムが持つメニューアイテムの取得 Declare Function menuFormRepresentation Lib "Cocoa" Selector "menuFormRepresentation" (receiver As Ptr) As Ptr Dim menu1 As Ptr = menuFormRepresentation(segment0) // メニューアイテムが持つメニューの取得(各サブメニューアイテムはメニューに付加しているため) Declare Function submenu Lib "Cocoa" Selector "submenu" (receiver As Ptr) As Ptr Dim menuSub1 As Ptr = submenu(menu1) // 各サブメニューアイテムを配列で取得 Declare Function itemArray Lib "Cocoa" Selector "itemArray" (receiver As Ptr) As Ptr Dim menuAry1 As Ptr = itemArray(menuSub1) // 配列要素数の取得 Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer Dim cnt As Integer = count(menuAry1) // 初めに全クリア for i As Integer = 0 to cnt-1 setState(objectAtIndex(menuAry1, i), 0) // メニュー setSelected(segment1, false, i) // セグメントコントロール next // 指定されたアイテムをオンに setState(objectAtIndex(menuAry1, idx), 1) // メニューアイテムはチェック setSelected(segment1, true, idx) // セグメンテッドコントロールはプッシュ End Sub
- 以下をWindow1にペースト
Protected Sub ToolbarDispMode() // 起動時に一度実行すればいいので、完了済であれば戻る if TBOFMdone=true then return end if // ツールバーの取得 declare function toolbar lib "Cocoa" selector "toolbar" (obj_id as Integer) as Ptr Dim pnt1 As Ptr = toolbar(Window1.Handle) // 現在のモードを取得 Declare Function displayMode Lib "Cocoa" Selector "displayMode" (receiver As Ptr) As Integer Dim mode As Integer = displayMode(pnt1) // 現在のモードと異なる値をセット Dim temp As Integer select case mode case 0 temp=2 case 1 temp=2 case 2 temp=3 case 3 temp=1 else temp=1 end select // 現在のモードと異なる値をセット後、現在の値に戻す Declare Sub setDisplayMode Lib "Cocoa" Selector "setDisplayMode:" (receiver As Ptr, value As Integer) setDisplayMode(pnt1, temp) // 0=Default, 1=Icon & Text, 2=Icon Only, 3=Text Only setDisplayMode(pnt1, mode) // 0=Default, 1=Icon & Text, 2=Icon Only, 3=Text Only // 完了済フラグ・オン TBOFMdone=true End Sub
- 以下をWindow1にペースト
Protected Sub ToolbarItemClicked(sender As Ptr) Declare Function description Lib "Cocoa" Selector "description" (receiver As Ptr) As CFStringRef Dim name As String = description(sender) if InStrB(name,"NSSegmentedControl")>0 then // セグメンテッドコントロール(ボタン型やメニュー型を含む) Declare Function cell Lib "Cocoa" Selector "cell" (receiver As Ptr) As Ptr Declare Function tagForSegment Lib "Cocoa" Selector "tagForSegment:" (receiver As Ptr, seg As Integer) As Integer Declare Function selectedSegment Lib "Cocoa" Selector "selectedSegment" (receiver As Ptr) As Integer Declare Sub setSelected Lib "Cocoa" Selector "setSelected:forSegment:" (receiver As Ptr, selected As Boolean, no As Integer) Dim tagno As Integer = tagForSegment(cell(sender), selectedSegment(sender)) select case tagno case 501 Listbox1.AddRow "Selected Segment (Btn) = Icon View" case 502 Listbox1.AddRow "Selected Segment (Btn) = List View" case 503 Listbox1.AddRow "Selected Segment (Btn) = Column View" case 504 Listbox1.AddRow "Selected Segment (Btn) = Flow View" case 601 setSelected(sender, false, 0) Listbox1.AddRow "Selected Button = Share" case 611 setSelected(sender, false, 0) Listbox1.AddRow "Selected Button = Edit Tag" case 621 setSelected(sender, false, 0) Listbox1.AddRow "Selected Button = Quick Look" case 701 setSelected(sender, false, 0) Listbox1.AddRow "Selected Button = Go Left" case 702 setSelected(sender, false, 1) Listbox1.AddRow "Selected Button = Go Right" end select elseif InStrB(name,"NSMenuItem")>0 then // メニュー付きセグメンテッドコントロールのメニュー/テキストオンリー時のメニュー/オーバーフローメニュー Declare Function tag Lib "Cocoa" Selector "tag" (receiver As Ptr) As Integer Dim tagno As Integer = tag(sender) select case tagno // メニュー(テキストオンリー時のメニュー/オーバーフローメニューのサブメニュー含む) case 101 // 名前 Listbox1.AddRow "Selected Menu = 名前" case 102 // 種類 Listbox1.AddRow "Selected Menu = 種類" case 104 // なし Listbox1.AddRow "Selected Menu = なし" case 201 // 新規フォルダ Listbox1.AddRow "Selected Menu = 新規フォルダ" case 202 // 新規ウィンドウで開く Listbox1.AddRow "Selected Menu = 新規ウィンドウで開く" case 901 // 開く 1 Listbox1.AddRow "Selected Menu = 開く 1" case 902 // 開く 2 Listbox1.AddRow "Selected Menu = 開く 2" case 206 // 閉じる Listbox1.AddRow "Selected Menu = 閉じる" end select select case tagno // テキストオンリー時のメニュー/オーバーフローメニュー case 501 Listbox1.AddRow "Selected Segment (Menu) = Icon View" SetSegmentState(501,sender,0) // チェックマークとボタンプッシュの付け替え case 502 Listbox1.AddRow "Selected Segment (Menu) = List View" SetSegmentState(501,sender,1) // チェックマークとボタンプッシュの付け替え case 503 Listbox1.AddRow "Selected Segment (Menu) = Column View" SetSegmentState(501,sender,2) // チェックマークとボタンプッシュの付け替え case 504 Listbox1.AddRow "Selected Segment (Menu) = Flow View" SetSegmentState(501,sender,3) // チェックマークとボタンプッシュの付け替え case 601 Listbox1.AddRow "Selected Button = Share" case 611 Listbox1.AddRow "Selected Button = Edit Tag" case 621 Listbox1.AddRow "Selected Button = Quick Look" case 701 Listbox1.AddRow "Selected Button = Go Left" case 702 Listbox1.AddRow "Selected Button = Go Right" end select elseif InStrB(name,"NSSearchField")>0 then // サーチフィールド Declare Function stringValue Lib "Cocoa" Selector "stringValue" (receiver As Ptr) As CFStringRef Listbox1.AddRow "Search Word = "+stringValue(sender) // 検索文字列 else msgBox "No Define :"+name end if End Sub
- 以下をWindow1にペースト
Protected Function ToolbarValidateMenuItem(sender As Ptr) as Boolean // このメソッドはメニューが表示される度に呼ばれる(?)ので、戻り値を指定すれば反映される。 // デフォルトはEnable Dim enable As Boolean = true // 渡ってきたメニューアイテムからタグを取得 Declare Function tag Lib "Cocoa" Selector "tag" (receiver As Ptr) As Integer Dim tagno As Integer = tag(sender) // メニューアイテム(タグ)ごとの処理 select case tagno case 101 // 名前 case 102 // 種類 case 201 // 新規フォルダ case 202 // 新規ウィンドウで開く // フラグが無効ならDisableに if EnableNewWin then enable = false end if case 801 // サーチフィールドは常にDisable enable = false end select return enable End Function
- 以下をWindow1にペースト
Private Property EnableNewWin as Boolean
- 以下をWindow1にペースト
Protected Property TBOFMdone as Boolean = false
- 新規クラスを作成(名前は、ここでは「NSToolbar」とした。)
- 以下をNSToolbarにペースト
Private Sub ActionDelegate(sender As Ptr)
- 以下をNSToolbarにペースト
Private Function ActionDelegate1(sender As Ptr) as Boolean
- 以下をNSToolbarにペースト
Public Sub Constructor(win As Integer, action As ActionDelegate, action1 As ActionDelegate1) // 外部指定されたパラメータを保持 ActionHandler = action // クラス生成元でActionを受け取るメソッドを登録 ActionHandler1 = action1 // クラス生成元でActionを受け取るメソッドを登録 // NSToolbarを継承したインスタンスを作成して、ウィンドウに貼り付ける makeToolbarInstance(win) End Sub
- 以下をNSToolbarにペースト
Private Shared Sub actionEvent(id As Ptr, SEL As CString, sender As Ptr) // インスタンスメソッドに渡す ActionHandler.Invoke(sender) // クラス生成元でActionを受け取るメソッドを呼び出す End Sub
- 以下をNSToolbarにペースト
Private Shared Function makeBtnMenu(itemIdentifier As String) as Ptr Dim list(-1) As String select case itemIdentifier case ToolbarItemIdentifierSort list.Append "名前" list.Append "種類" list.Append "---" list.Append "なし" return makeMenu(itemIdentifier,list,101) case ToolbarItemIdentifierAction list.Append "新規フォルダ" list.Append "新規ウィンドウで開く" list.Append "sub:2:開く" list.Append "開く 1" list.Append "開く 2" list.Append "閉じる" return makeMenu(itemIdentifier,list,201) end select End Function
- 以下をNSToolbarにペースト
Private Shared Function makeDelegate() as Ptr // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 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 // クラス名をmyNSToolbarDelegate(名前は任意。少なくとも今回のケースでは参照されない。)、メタクラス名をNSObjectにして、生成 Dim newClassId As Ptr = objc_allocateClassPair(NSClassFromString("NSObject"), "myNSToolbarDelegate", 0) // ランタイムに登録(参照を可能とするため) objc_registerClassPair newClassId // Delegateの対象となるメソッドを追加(toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:をXojo側で用意したtoolbarItemForItemIdentifierWillBeInsertedIntoToolbarメソッドで受け取る。) if not class_addMethod (newClassId, NSSelectorFromString("toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:"), AddressOf toolbarItemForItemIdentifierWillBeInsertedIntoToolbar, "@@:@@c") then msgBox "error." return nil end if // Delegateの対象となるメソッドを追加(toolbarAllowedItemIdentifiers:をXojo側で用意したtoolbarAllowedItemIdentifiersメソッドで受け取る。) if not class_addMethod (newClassId, NSSelectorFromString("toolbarAllowedItemIdentifiers:"), AddressOf toolbarAllowedItemIdentifiers, "@@:@") then msgBox "error." return nil end if // Delegateの対象となるメソッドを追加(toolbarDefaultItemIdentifiers:をXojo側で用意したtoolbarDefaultItemIdentifiersメソッドで受け取る。) if not class_addMethod (newClassId, NSSelectorFromString("toolbarDefaultItemIdentifiers:"), AddressOf toolbarDefaultItemIdentifiers, "@@:@") then msgBox "error." return nil end if // Delegateの対象となるメソッドを追加(toolbarSelectableItemIdentifiers:をXojo側で用意したtoolbarSelectableItemIdentifiersメソッドで受け取る。) if not class_addMethod (newClassId, NSSelectorFromString("toolbarSelectableItemIdentifiers:"), AddressOf toolbarSelectableItemIdentifiers, "@@:@") then msgBox "error." return nil end if // 上記で生成したクラスのインスタンスを作成 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 delegateId As Ptr = init(alloc(newClassId)) // インスタンスを返す return delegateId End Function
- 以下をNSToolbarにペースト
Private Shared Function makeMenu(itemIdentifier As String, list() As String, tag0 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 alloc Lib "Cocoa" Selector "alloc" (receiver As Ptr) As Ptr Declare Function init Lib "Cocoa" Selector "init" (receiver As Ptr) As Ptr Declare Function initWithTitleActKey Lib "Cocoa" Selector "initWithTitle:action:keyEquivalent:" (receiver As Ptr, title As CFStringRef, act As Ptr, key As CFStringRef) As Ptr Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr) Declare Sub setTitle Lib "Cocoa" Selector "setTitle:" (receiver As Ptr, title As CFStringRef) Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr) Declare Sub setTag Lib "Cocoa" Selector "setTag:" (receiver As Ptr, tag As Integer) Declare Sub setSubmenu Lib "Cocoa" Selector "setSubmenu:" (receiver As Ptr, item As Ptr) Declare Sub addItem Lib "Cocoa" Selector "addItem:" (receiver As Ptr, item As Ptr) Declare Function separatorItem Lib "Cocoa" Selector "separatorItem" (receiver As Ptr) As Ptr Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr) // メニューの生成と初期化 Dim menu1 As Ptr = NSClassFromString("NSMenu") menu1 = alloc(menu1) menu1 = init(menu1) // メニュー項目 Dim menu2, menu20, menu21 As Ptr Dim pos, j, cnt As Integer pos=0 do // メニュー項目クラスの取得 menu2 = NSClassFromString("NSMenuItem") if list(pos)="---" then // セパレータ menu2 = separatorItem(menu2) elseif Left(list(pos),4)="sub:" then // サブメニューを持ったメニュー // メニュー項目の生成と初期化 menu2 = alloc(menu2) menu2 = init(menu2) setTitle(menu2, NthField(list(pos),":",3)) // Target/Actionを設定 setTarget(menu2, makeTarget(itemIdentifier)) setAction(menu2, NSSelectorFromString("action:")) // メニューの生成と初期化(サブメニューはメニューに付加する) menu20 = NSClassFromString("NSMenu") menu20 = alloc(menu20) menu20 = init(menu20) cnt=Val(NthField(list(pos),":",2)) for j=1 to cnt // サブメニューを生成 menu21 = NSClassFromString("NSMenuItem") menu21 = alloc(menu21) menu21 = initWithTitleActKey(menu21, list(pos+j), NSSelectorFromString("action:"), "") // ここでActionも設定 // Targetを設定 setTarget(menu21, makeTarget(itemIdentifier)) // タグ setTag(menu21, 900+j) // タグだけ判別するので、同じActionメソッドを使用するメニュー間で固有の数値にする(今回の例では一つしかないので決め打ちしているが、複数ある場合は工夫が必要) // メニューにサブメニューを追加 addItem(menu20, menu21) // 解放 release(menu21) next pos=pos+cnt // メニュー項目にメニュー(サブメニューを持っている)をセット setSubmenu(menu2, menu20) // 解放 release(menu20) else // 通常のメニュー // メニュー項目の生成と初期化 menu2 = alloc(menu2) menu2 = initWithTitleActKey(menu2, list(pos), NSSelectorFromString("action:"), "") // ここでActionも設定 // Targetを設定 setTarget(menu2, makeTarget(itemIdentifier)) // タグ setTag(menu2, tag0+pos) // タグだけ判別するので、同じActionメソッドを使用するメニュー間で固有の数値にする end if // メニューにメニュー項目を追加 addItem(menu1, menu2) // リストが終了したら抜ける pos=pos+1 if pos>list.Ubound then exit end if loop // メニューを返す return menu1 End Function
- 以下をNSToolbarにペースト
Private Shared Function makeTbarMenu(itemIdentifier As String) as Ptr Dim list(-1) As String select case itemIdentifier case SegmentedControlToolbarItemIdentifier list.Append "Segment" // オーバーフローメニューではタイトルとして使われる。 list.Append "Seg 1" // 以下は、テキストのみモードではドロップダウンメニュー、オーバーフローメニューではサブメニューとして使われる。 list.Append "Seg 2" list.Append "Seg 3" list.Append "Seg 4" return makeTbarMenu2(itemIdentifier,list,501) case SegmentedControlPairToolbarItemIdentifier list.Append "Pair" // オーバーフローメニューではタイトルとして使われる。 list.Append "Pair 1" // 以下は、テキストのみモードではドロップダウンメニュー、オーバーフローメニューではサブメニューとして使われる。 list.Append "Pair 2" return makeTbarMenu2(itemIdentifier,list,701) case ToolbarItemIdentifierSort list.Append "Btn Menu 1" // オーバーフローメニューではタイトルとして使われる。 list.Append "名前" // 以下は、テキストのみモードではドロップダウンメニュー、オーバーフローメニューではサブメニューとして使われる。 list.Append "種類" list.Append "---" list.Append "なし" return makeTbarMenu2(itemIdentifier,list,101) case ToolbarItemIdentifierAction list.Append "Btn Menu 2" // オーバーフローメニューではタイトルとして使われる。 list.Append "新規フォルダ" // 以下は、テキストのみモードではドロップダウンメニュー、オーバーフローメニューではサブメニューとして使われる。 list.Append "新規ウィンドウで開く" list.Append "sub:2:開く" list.Append "開く 1" list.Append "開く 2" list.Append "閉じる" return makeTbarMenu2(itemIdentifier,list,201) end select End Function
- 以下をNSToolbarにペースト
Private Shared Function makeTbarMenu2(itemIdentifier As String, list() As String, tag0 As Integer) as Ptr // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr // メニュー項目の生成と初期化 Dim menu2 As Ptr = NSClassFromString("NSMenuItem") Declare Function alloc Lib "Cocoa" Selector "alloc" (receiver As Ptr) As Ptr Declare Function init Lib "Cocoa" Selector "init" (receiver As Ptr) As Ptr menu2 = init(alloc(menu2)) Declare Sub setTitle Lib "Cocoa" Selector "setTitle:" (receiver As Ptr, title As CFStringRef) setTitle(menu2, list(0)) Declare Sub setImage Lib "Cocoa" Selector "setImage:" (receiver As Ptr, img As Ptr) setImage(menu2, nil) // メニューに付加するアイコン。オーバーフローメニューの場合はnilにしないと、タイトルがずれる(通常のメニューでも弊害はなさそう) Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr) setTarget(menu2, makeTarget(itemIdentifier)) Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr) setAction(menu2, NSSelectorFromString("action:")) // リストの先頭は使用済なので削除する list.Remove(0) // サブメニュー生成 Dim menu20 As Ptr = MakeMenu(itemIdentifier,list,tag0) // メニュー項目にメニュー(サブメニューを持っている)をセット Declare Sub setSubmenu Lib "Cocoa" Selector "setSubmenu:" (receiver As Ptr, item As Ptr) setSubmenu(menu2, menu20) // clean up Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr) release(menu20) // メニュー項目を返す return menu2 End Function
- 以下をNSToolbarにペースト
Private Shared Function setToolItemSearch(itemIdentifier As String) as Ptr // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 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" (receiver As Ptr) As Ptr Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr) Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr) // サーチフィールド初期化 Dim search1 As Ptr = NSClassFromString("NSSearchField") search1 = alloc(search1) Declare Function initWithFrame Lib "Cocoa" Selector "initWithFrame:" (receiver As Ptr, identifier As NSRect) As Ptr search1 = initWithFrame(search1, NSMakeRect(0,0,150,24)) // アイテム初期化 Dim toolbarItem As Ptr = NSClassFromString("NSToolbarItem") toolbarItem = alloc(toolbarItem) Declare Function initWithItemIdentifier Lib "Cocoa" Selector "initWithItemIdentifier:" (receiver As Ptr, identifier As CFStringRef) As Ptr toolbarItem = initWithItemIdentifier(toolbarItem, itemIdentifier) // ラベル/ツールチップ設定 Declare Sub setLabel Lib "Cocoa" Selector "setLabel:" (receiver As Ptr, label As CFStringRef) setLabel(toolbarItem, "NSSearchField") Declare Sub setPaletteLabel Lib "Cocoa" Selector "setPaletteLabel:" (receiver As Ptr, label As CFStringRef) setPaletteLabel(toolbarItem, "Search") Declare Sub setToolTip Lib "Cocoa" Selector "setToolTip:" (receiver As Ptr, text As CFStringRef) setToolTip(toolbarItem, "Search Your Document") // サーチフィールドクリック後、直ちに文字入力を可能とするための設定(2016.06.28追加) Declare Sub refusesFirstResponder Lib "Cocoa" Selector "setRefusesFirstResponder:" (receiver As Ptr, flg As Boolean) refusesFirstResponder(search1,true) // メニューのTarget/Action設定(テキストオンリー時のメニュー/オーバーフローメニュー対策) Declare Function menuFormRepresentation Lib "Cocoa" Selector "menuFormRepresentation" (receiver As Ptr) As Ptr Dim menuItem As Ptr = menuFormRepresentation(toolbarItem) // 既存のmenuItemを持ってくる setTarget(menuItem, makeTarget(itemIdentifier)) // Actionの受け口となるメソッドを定義 setAction(menuItem, NSSelectorFromString("action:")) Declare Sub setTagMenu Lib "Cocoa" Selector "setTag:" (receiver As Ptr, tag As Integer) setTagMenu(menuItem, 801) Declare Sub setImage Lib "Cocoa" Selector "setImage:" (receiver As Ptr, img As Ptr) setImage(menuItem, nil) // アイテムのビューにサーチフィールドを設定 Declare Sub setView Lib "Cocoa" Selector "setView:" (receiver As Ptr, actionTarget As Ptr) setView(toolbarItem, search1) // Target/Action設定 setTarget(search1, makeTarget(itemIdentifier)) // Actionの受け口となるメソッドを定義 setAction(search1, NSSelectorFromString("action:")) // 検索オプション設定 Declare Function cell Lib "Cocoa" Selector "cell" (receiver As Ptr) As Ptr Dim cell1 As Ptr = cell(search1) // NSSearchFieldCellを取得 Declare Sub setSendsWholeSearchString Lib "Cocoa" Selector "setSendsWholeSearchString:" (receiver As Ptr, flag As Boolean) setSendsWholeSearchString(cell1, true) // false:キーストローク毎にActionが発生, true:Retrnキーを押した時点でActionが発生 Declare Sub sendsSearchStringImmediately Lib "Cocoa" Selector "setSendsSearchStringImmediately:" (receiver As Ptr, flag As Boolean) sendsSearchStringImmediately(cell1, false) // false:文字をある程度溜めてからActionが発生, true:文字を入力すると直ちにActionが発生 // 生成したアイテムを返す return toolbarItem End Function
- 以下をNSToolbarにペースト
Private Shared Function setToolItemSegment(itemIdentifier As String) as Ptr // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 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" (receiver As Ptr) As Ptr Dim image0 As Ptr = NSClassFromString("NSImage") Dim image1 As Ptr = NSClassFromString("NSImage") Dim image2 As Ptr = NSClassFromString("NSImage") Dim image3 As Ptr = NSClassFromString("NSImage") Declare Function imageNamed Lib "Cocoa" Selector "imageNamed:" (receiver As Ptr, name As CFStringRef) As Ptr image0 = imageNamed(image0, "NSIconViewTemplate") image1 = imageNamed(image1, "NSListViewTemplate") image2 = imageNamed(image2, "NSColumnViewTemplate") image3 = imageNamed(image3, "NSFlowViewTemplate") Dim segment1 As Ptr = NSClassFromString("NSSegmentedControl") segment1 = alloc(segment1) Declare Function initWithFrame Lib "Cocoa" Selector "initWithFrame:" (receiver As Ptr, identifier As NSRect) As Ptr segment1 = initWithFrame(segment1, NSMakeRect(0,0,111,24)) Declare Sub setSegmentCount Lib "Cocoa" Selector "setSegmentCount:" (receiver As Ptr, cnt As Integer) setSegmentCount(segment1, 4) Declare Sub setImage Lib "Cocoa" Selector "setImage:forSegment:" (receiver As Ptr, image As Ptr, no As Integer) setImage(segment1, image0, 0) setImage(segment1, image1, 1) setImage(segment1, image2, 2) setImage(segment1, image3, 3) Declare Sub setWidth Lib "Cocoa" Selector "setWidth:forSegment:" (receiver As Ptr, w As CGFloat, no As Integer) setWidth(segment1, 26, 0) setWidth(segment1, 26, 1) setWidth(segment1, 26, 2) setWidth(segment1, 26, 3) Declare Sub setSelected Lib "Cocoa" Selector "setSelected:forSegment:" (receiver As Ptr, selected As Boolean, no As Integer) setSelected(segment1, true, 0) setSelected(segment1, false, 1) setSelected(segment1, false, 2) setSelected(segment1, false, 3) Declare Function cell Lib "Cocoa" Selector "cell" (receiver As Ptr) As Ptr Declare Sub setTag Lib "Cocoa" Selector "setTag:forSegment:" (receiver As Ptr, tag As Integer, no As Integer) setTag(cell(segment1), 501, 0) setTag(cell(segment1), 502, 1) setTag(cell(segment1), 503, 2) setTag(cell(segment1), 504, 3) Declare Sub setToolTip Lib "Cocoa" Selector "setToolTip:forSegment:" (receiver As Ptr, text As CFStringRef, no As Integer) setToolTip(cell(segment1), "Segmented Control 0", 0) setToolTip(cell(segment1), "Segmented Control 1", 1) setToolTip(cell(segment1), "Segmented Control 2", 2) setToolTip(cell(segment1), "Segmented Control 3", 3) Dim toolbarItem As Ptr = NSClassFromString("NSToolbarItem") toolbarItem = alloc(toolbarItem) Declare Function initWithItemIdentifier Lib "Cocoa" Selector "initWithItemIdentifier:" (receiver As Ptr, identifier As CFStringRef) As Ptr toolbarItem = initWithItemIdentifier(toolbarItem, itemIdentifier) Declare Sub setLabel Lib "Cocoa" Selector "setLabel:" (receiver As Ptr, label As CFStringRef) setLabel(toolbarItem, "NSSegmentedControl") Declare Sub setPaletteLabel Lib "Cocoa" Selector "setPaletteLabel:" (receiver As Ptr, label As CFStringRef) setPaletteLabel(toolbarItem, "Segmented Control") 'Declare Sub setTag2 Lib "Cocoa" Selector "setTag:" (receiver As Ptr, tag As Integer) 'setTag2(toolbarItem, 500) // メニューのTarget/Action設定(テキストオンリー時のメニュー/オーバーフローメニュー対策) Dim menu1 As Ptr = makeTbarMenu(itemIdentifier) // メニュー生成 Declare Sub setMenuFormRepresentation Lib "Cocoa" Selector "setMenuFormRepresentation:" (receiver As Ptr, menu As Ptr) setMenuFormRepresentation(toolbarItem, menu1) // 戻す // メニューにチェックマークをつける(ボタンと同じく、0番セグメントをオンに) Declare Function submenu Lib "Cocoa" Selector "submenu" (receiver As Ptr) As Ptr Dim menuSub1 As Ptr = submenu(menu1) Declare Function itemArray Lib "Cocoa" Selector "itemArray" (receiver As Ptr) As Ptr Dim menuAry1 As Ptr = itemArray(menuSub1) Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr Dim menuItem1 As Ptr = objectAtIndex(menuAry1, 0) Declare Sub setState Lib "Cocoa" Selector "setState:" (receiver As Ptr, flg As Integer) setState(menuItem1, 1) Declare Sub setView Lib "Cocoa" Selector "setView:" (receiver As Ptr, actionTarget As Ptr) setView(toolbarItem, segment1) Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr) setTarget(toolbarItem, makeTarget(itemIdentifier)) // Actionの受け口となるメソッドを定義 Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr) setAction(toolbarItem, NSSelectorFromString("action:")) return toolbarItem End Function
- 以下をNSToolbarにペースト
Private Shared Function setToolItemSegment1(itemIdentifier As String, label As String, labelL As String, tooltip As String, icon As String, tag 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 alloc Lib "Cocoa" Selector "alloc" (receiver As Ptr) As Ptr Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr) Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr) Dim image1 As Ptr = NSClassFromString("NSImage") Declare Function imageNamed Lib "Cocoa" Selector "imageNamed:" (receiver As Ptr, name As CFStringRef) As Ptr image1 = imageNamed(image1, icon) // セグメンテッドコントロール初期化 Dim segment1 As Ptr = NSClassFromString("NSSegmentedControl") segment1 = alloc(segment1) Declare Function initWithFrame Lib "Cocoa" Selector "initWithFrame:" (receiver As Ptr, identifier As NSRect) As Ptr segment1 = initWithFrame(segment1, NSMakeRect(0,0,38,24)) // セグメント数/ラベル/選択状態/タグ設定 Declare Sub setSegmentCount Lib "Cocoa" Selector "setSegmentCount:" (receiver As Ptr, cnt As Integer) setSegmentCount(segment1, 1) Declare Sub setImage Lib "Cocoa" Selector "setImage:forSegment:" (receiver As Ptr, image As Ptr, no As Integer) setImage(segment1, image1, 0) Declare Sub setWidth Lib "Cocoa" Selector "setWidth:forSegment:" (receiver As Ptr, w As CGFloat, no As Integer) setWidth(segment1, 34, 0) Declare Sub setSelected Lib "Cocoa" Selector "setSelected:forSegment:" (receiver As Ptr, selected As Boolean, no As Integer) setSelected(segment1, false, 0) // 非選択 Declare Function cell Lib "Cocoa" Selector "cell" (receiver As Ptr) As Ptr Declare Sub setTag Lib "Cocoa" Selector "setTag:forSegment:" (receiver As Ptr, tag As Integer, no As Integer) setTag(cell(segment1), tag, 0) // タグだけ判別するので、同じActionメソッドを使用するセグメント間で固有の数値にする // アイテム初期化 Dim toolbarItem As Ptr = NSClassFromString("NSToolbarItem") toolbarItem = alloc(toolbarItem) Declare Function initWithItemIdentifier Lib "Cocoa" Selector "initWithItemIdentifier:" (receiver As Ptr, identifier As CFStringRef) As Ptr toolbarItem = initWithItemIdentifier(toolbarItem, itemIdentifier) // ラベル/ツールチップ設定 Declare Sub setLabel Lib "Cocoa" Selector "setLabel:" (receiver As Ptr, label As CFStringRef) setLabel(toolbarItem, label) Declare Sub setPaletteLabel Lib "Cocoa" Selector "setPaletteLabel:" (receiver As Ptr, label As CFStringRef) setPaletteLabel(toolbarItem, labelL) Declare Sub setToolTip Lib "Cocoa" Selector "setToolTip:" (receiver As Ptr, text As CFStringRef) setToolTip(toolbarItem, tooltip) // メニューのTarget/Action設定(テキストオンリー時のメニュー/オーバーフローメニュー対策) Declare Function menuFormRepresentation Lib "Cocoa" Selector "menuFormRepresentation" (receiver As Ptr) As Ptr Dim menuItem As Ptr = menuFormRepresentation(toolbarItem) // 既存のmenuItemを持ってくる setTarget(menuItem, makeTarget(itemIdentifier)) // Actionの受け口となるメソッドを定義 setAction(menuItem, NSSelectorFromString("action:")) Declare Sub setTagMenu Lib "Cocoa" Selector "setTag:" (receiver As Ptr, tag As Integer) setTagMenu(menuItem, tag) // セグメントに設定したものと同じ数値にする Declare Sub setImage Lib "Cocoa" Selector "setImage:" (receiver As Ptr, img As Ptr) setImage(menuItem, nil) // アイテムのビューにセグメンテッドコントロールを設定 Declare Sub setView Lib "Cocoa" Selector "setView:" (receiver As Ptr, actionTarget As Ptr) setView(toolbarItem, segment1) // Target/Action設定 setTarget(toolbarItem, makeTarget(itemIdentifier)) // Actionの受け口となるメソッドを定義 setAction(toolbarItem, NSSelectorFromString("action:")) // 生成したアイテムを返す return toolbarItem End Function
- 以下をNSToolbarにペースト(メニューを示す下向き矢印の表示を、標準機能に改めた。(2021.05.11))
以下の旧版は、実装方法の一つではあるので、参考まで。Private Shared Function setToolItemSegment1Menu(itemIdentifier As String, label As String, labelL As String, tooltip As String, icon As String, menu 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 alloc Lib "Cocoa" Selector "alloc" (receiver As Ptr) As Ptr // 標準イメージの取得 Dim image1 As Ptr = NSClassFromString("NSImage") Declare Function imageNamed Lib "Cocoa" Selector "imageNamed:" (receiver As Ptr, name As CFStringRef) As Ptr image1 = imageNamed(image1, icon) Declare Sub setTemplate Lib "Cocoa" Selector "setTemplate:" (receiver As Ptr, flg As Boolean) setTemplate(image1, true) // 選択時のカラー反転や、ディアクティベート時のグレー化 // セグメンテッドコントロール初期化 Dim segment1 As Ptr = NSClassFromString("NSSegmentedControl") segment1 = alloc(segment1) Declare Function initWithFrame Lib "Cocoa" Selector "initWithFrame:" (receiver As Ptr, identifier As NSRect) As Ptr segment1 = initWithFrame(segment1, NSMakeRect(0,0,40,22)) // セグメント数/イメージ/選択状態/メニュー Declare Sub setSegmentCount Lib "Cocoa" Selector "setSegmentCount:" (receiver As Ptr, cnt As Integer) setSegmentCount(segment1, 1) Declare Sub setImage Lib "Cocoa" Selector "setImage:forSegment:" (receiver As Ptr, image As Ptr, no As Integer) setImage(segment1, image1, 0) Declare Sub setWidth Lib "Cocoa" Selector "setWidth:forSegment:" (receiver As Ptr, w As CGFloat, no As Integer) setWidth(segment1, 36, 0) Declare Sub setSelected Lib "Cocoa" Selector "setSelected:forSegment:" (receiver As Ptr, selected As Boolean, no As Integer) setSelected(segment1, false, 0) // 非選択 Declare Function cell Lib "Cocoa" Selector "cell" (receiver As Ptr) As Ptr Declare Sub setMenu Lib "Cocoa" Selector "setMenu:forSegment:" (receiver As Ptr, menu As Ptr, no As Integer) setMenu(cell(segment1), menu, 0) Declare Sub setShowsMenuIndicator Lib "Cocoa" Selector "setShowsMenuIndicator:forSegment:" (receiver As Ptr, flg As Boolean, no As Integer) setShowsMenuIndicator(segment1, true, 0) // メニューを示す下向き矢印表示 // アイテム初期化 Dim toolbarItem As Ptr = NSClassFromString("NSToolbarItem") toolbarItem = alloc(toolbarItem) Declare Function initWithItemIdentifier Lib "Cocoa" Selector "initWithItemIdentifier:" (receiver As Ptr, identifier As CFStringRef) As Ptr toolbarItem = initWithItemIdentifier(toolbarItem, itemIdentifier) // ラベル/ツールチップ設定 Declare Sub setLabel Lib "Cocoa" Selector "setLabel:" (receiver As Ptr, label As CFStringRef) setLabel(toolbarItem, label) Declare Sub setPaletteLabel Lib "Cocoa" Selector "setPaletteLabel:" (receiver As Ptr, label As CFStringRef) setPaletteLabel(toolbarItem, labelL) Declare Sub setToolTip Lib "Cocoa" Selector "setToolTip:" (receiver As Ptr, text As CFStringRef) setToolTip(toolbarItem, labelL) // メニューのTarget/Action設定(テキストオンリー時のメニュー/オーバーフローメニュー対策) Declare Sub setMenuFormRepresentation Lib "Cocoa" Selector "setMenuFormRepresentation:" (receiver As Ptr, menu As Ptr) setMenuFormRepresentation(toolbarItem, makeTbarMenu(itemIdentifier)) // 戻す // アイテムのビューにセグメンテッドコントロールを設定 Declare Sub setView Lib "Cocoa" Selector "setView:" (receiver As Ptr, actionTarget As Ptr) setView(toolbarItem, segment1) // 生成したアイテムを返す return toolbarItem End Function
Private Shared Function setToolItemSegment1Menu(itemIdentifier As String, label As String, labelL As String, tooltip As String, icon As String, menu 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 alloc Lib "Cocoa" Selector "alloc" (receiver As Ptr) As Ptr // イメージの生成(標準イメージを貼り付けるベース) Dim image0 As Ptr = NSClassFromString("NSImage") image0 = alloc(image0) Declare Function initWithSize Lib "Cocoa" Selector "initWithSize:" (receiver As Ptr, size As NSSize) As Ptr image0 = initWithSize(image0, NSMakeSize(40,24)) // 標準イメージの取得 Dim image1 As Ptr = NSClassFromString("NSImage") Dim image2 As Ptr = NSClassFromString("NSImage") Declare Function imageNamed Lib "Cocoa" Selector "imageNamed:" (receiver As Ptr, name As CFStringRef) As Ptr image1 = imageNamed(image1, icon) image2 = imageNamed(image2, "NSDropDownIndicatorTemplate") // イメージの合成 Declare Sub lockFocus Lib "Cocoa" Selector "lockFocus" (receiver As Ptr) lockFocus(image0) Declare Sub compositeToPoint Lib "Cocoa" Selector "compositeToPoint:operation:" (receiver As Ptr, item1 As NSPoint, item2 As Integer) compositeToPoint(image1, NSMakePoint(5, 5), 2) compositeToPoint(image2, NSMakePoint(25, 10), 2) Declare Sub unlockFocus Lib "Cocoa" Selector "unlockFocus" (receiver As Ptr) unlockFocus(image0) Declare Sub setTemplate Lib "Cocoa" Selector "setTemplate:" (receiver As Ptr, flg As Boolean) setTemplate(image0, true) // 選択時のカラー反転や、ディアクティベート時のグレー化 // セグメンテッドコントロール初期化 Dim segment1 As Ptr = NSClassFromString("NSSegmentedControl") segment1 = alloc(segment1) Declare Function initWithFrame Lib "Cocoa" Selector "initWithFrame:" (receiver As Ptr, identifier As NSRect) As Ptr segment1 = initWithFrame(segment1, NSMakeRect(0,0,40,22)) // セグメント数/イメージ/選択状態/メニュー Declare Sub setSegmentCount Lib "Cocoa" Selector "setSegmentCount:" (receiver As Ptr, cnt As Integer) setSegmentCount(segment1, 1) Declare Sub setImage Lib "Cocoa" Selector "setImage:forSegment:" (receiver As Ptr, image As Ptr, no As Integer) setImage(segment1, image0, 0) Declare Sub setWidth Lib "Cocoa" Selector "setWidth:forSegment:" (receiver As Ptr, w As CGFloat, no As Integer) setWidth(segment1, 36, 0) Declare Sub setSelected Lib "Cocoa" Selector "setSelected:forSegment:" (receiver As Ptr, selected As Boolean, no As Integer) setSelected(segment1, false, 0) // 非選択 Declare Function cell Lib "Cocoa" Selector "cell" (receiver As Ptr) As Ptr Declare Sub setMenu Lib "Cocoa" Selector "setMenu:forSegment:" (receiver As Ptr, menu As Ptr, no As Integer) setMenu(cell(segment1), menu, 0) // アイテム初期化 Dim toolbarItem As Ptr = NSClassFromString("NSToolbarItem") toolbarItem = alloc(toolbarItem) Declare Function initWithItemIdentifier Lib "Cocoa" Selector "initWithItemIdentifier:" (receiver As Ptr, identifier As CFStringRef) As Ptr toolbarItem = initWithItemIdentifier(toolbarItem, itemIdentifier) // ラベル/ツールチップ設定 Declare Sub setLabel Lib "Cocoa" Selector "setLabel:" (receiver As Ptr, label As CFStringRef) setLabel(toolbarItem, label) Declare Sub setPaletteLabel Lib "Cocoa" Selector "setPaletteLabel:" (receiver As Ptr, label As CFStringRef) setPaletteLabel(toolbarItem, labelL) Declare Sub setToolTip Lib "Cocoa" Selector "setToolTip:" (receiver As Ptr, text As CFStringRef) setToolTip(toolbarItem, labelL) // メニューのTarget/Action設定(テキストオンリー時のメニュー/オーバーフローメニュー対策) Declare Sub setMenuFormRepresentation Lib "Cocoa" Selector "setMenuFormRepresentation:" (receiver As Ptr, menu As Ptr) setMenuFormRepresentation(toolbarItem, makeTbarMenu(itemIdentifier)) // 戻す // アイテムのビューにセグメンテッドコントロールを設定 Declare Sub setView Lib "Cocoa" Selector "setView:" (receiver As Ptr, actionTarget As Ptr) setView(toolbarItem, segment1) // 生成したアイテムを返す return toolbarItem End Function
- 以下をNSToolbarにペースト
Private Shared Function setToolItemSegmentPair(itemIdentifier As String) as Ptr // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 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" (receiver As Ptr) As Ptr // 標準イメージの取得 Dim image0 As Ptr = NSClassFromString("NSImage") Dim image1 As Ptr = NSClassFromString("NSImage") Declare Function imageNamed Lib "Cocoa" Selector "imageNamed:" (receiver As Ptr, name As CFStringRef) As Ptr image0 = imageNamed(image0, "NSGoLeftTemplate") image1 = imageNamed(image1, "NSGoRightTemplate") Declare Sub setTemplate Lib "Cocoa" Selector "setTemplate:" (receiver As Ptr, flg As Boolean) setTemplate(image0, true) // 選択時のカラー反転や、ディアクティベート時のグレー化 setTemplate(image1, true) // 選択時のカラー反転や、ディアクティベート時のグレー化 Dim segment1 As Ptr = NSClassFromString("NSSegmentedControl") segment1 = alloc(segment1) Declare Function initWithFrame Lib "Cocoa" Selector "initWithFrame:" (receiver As Ptr, identifier As NSRect) As Ptr segment1 = initWithFrame(segment1, NSMakeRect(0,0,51,24)) Declare Sub setSegmentCount Lib "Cocoa" Selector "setSegmentCount:" (receiver As Ptr, cnt As Integer) setSegmentCount(segment1, 2) Declare Sub setSegmentStyle Lib "Cocoa" Selector "setSegmentStyle:" (receiver As Ptr, cnt As Integer) setSegmentStyle(segment1, 8) // 8だとImageのTemplateが効かない > OS標準でもそのようなので仕様? Declare Sub setImage Lib "Cocoa" Selector "setImage:forSegment:" (receiver As Ptr, image As Ptr, no As Integer) setImage(segment1, image0, 0) setImage(segment1, image1, 1) Declare Sub setWidth Lib "Cocoa" Selector "setWidth:forSegment:" (receiver As Ptr, w As CGFloat, no As Integer) setWidth(segment1, 23, 0) setWidth(segment1, 23, 1) Declare Sub setSelected Lib "Cocoa" Selector "setSelected:forSegment:" (receiver As Ptr, selected As Boolean, no As Integer) setSelected(segment1, false, 0) setSelected(segment1, false, 1) Declare Function cell Lib "Cocoa" Selector "cell" (receiver As Ptr) As Ptr Declare Sub setTag Lib "Cocoa" Selector "setTag:forSegment:" (receiver As Ptr, tag As Integer, no As Integer) setTag(cell(segment1), 701, 0) setTag(cell(segment1), 702, 1) Declare Sub setToolTip Lib "Cocoa" Selector "setToolTip:forSegment:" (receiver As Ptr, text As CFStringRef, no As Integer) setToolTip(cell(segment1), "Segmented Control 0", 0) setToolTip(cell(segment1), "Segmented Control 1", 1) Dim toolbarItem As Ptr = NSClassFromString("NSToolbarItem") toolbarItem = alloc(toolbarItem) Declare Function initWithItemIdentifier Lib "Cocoa" Selector "initWithItemIdentifier:" (receiver As Ptr, identifier As CFStringRef) As Ptr toolbarItem = initWithItemIdentifier(toolbarItem, itemIdentifier) Declare Sub setLabel Lib "Cocoa" Selector "setLabel:" (receiver As Ptr, label As CFStringRef) setLabel(toolbarItem, "Button Pair") Declare Sub setPaletteLabel Lib "Cocoa" Selector "setPaletteLabel:" (receiver As Ptr, label As CFStringRef) setPaletteLabel(toolbarItem, "Button Pair") // メニューのTarget/Action設定(テキストオンリー時のメニュー/オーバーフローメニュー対策) Declare Sub setMenuFormRepresentation Lib "Cocoa" Selector "setMenuFormRepresentation:" (receiver As Ptr, menu As Ptr) setMenuFormRepresentation(toolbarItem, makeTbarMenu(itemIdentifier)) // 戻す Declare Sub setView Lib "Cocoa" Selector "setView:" (receiver As Ptr, actionTarget As Ptr) setView(toolbarItem, segment1) Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr) setTarget(toolbarItem, makeTarget(itemIdentifier)) // Actionの受け口となるメソッドを定義 Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr) setAction(toolbarItem, NSSelectorFromString("action:")) return toolbarItem End Function
- 以下をNSToolbarにペースト
Private Shared Function toolbarItemForItemIdentifierWillBeInsertedIntoToolbar(id as Ptr, sel as CString, toolbar As Ptr, itemIdentifier As CFStringRef, flag As Boolean) as Ptr if itemIdentifier = SearchToolbarItemIdentifier then return setToolItemSearch(itemIdentifier) elseif itemIdentifier = SegmentedControlToolbarItemIdentifier then return setToolItemSegment(itemIdentifier) elseif itemIdentifier = ToolbarItemIdentifierShare then return setToolItemSegment1(itemIdentifier,"Button 1","Button","Button","NSShareTemplate",601) elseif itemIdentifier = ToolbarItemIdentifierTag then return setToolItemSegment1(itemIdentifier,"Button 2","Button","Button","NSBookmarksTemplate",611) // 本来はタグアイコンを代替使用 elseif itemIdentifier = ToolbarItemIdentifierQuick then return setToolItemSegment1(itemIdentifier,"Button 3","Button","Button","NSQuickLookTemplate",621) elseif itemIdentifier = ToolbarItemIdentifierSort then return setToolItemSegment1Menu(itemIdentifier,"Btn Menu 1","Btn Menu 1","Btn Menu 1","NSHomeTemplate",makeBtnMenu(itemIdentifier)) // 本来はソートアイコンを代替使用 elseif itemIdentifier = ToolbarItemIdentifierAction then return setToolItemSegment1Menu(itemIdentifier,"Btn Menu 2","Btn Menu 2","Btn Menu 2","NSActionTemplate",makeBtnMenu(itemIdentifier)) elseif itemIdentifier = SegmentedControlPairToolbarItemIdentifier then return setToolItemSegmentPair(itemIdentifier) else return nil end if End Function
- 以下のNSToolbarのメソッドを前回プロジェクトからペースト
・makeTarget
・makeToolbarInstance
・toolbarAllowedItemIdentifiers
・toolbarDefaultItemIdentifiers
・toolbarSelectableItemIdentifiers
・validateMenuItem
- 以下のNSToolbarのプロパティを前回プロジェクトからペースト
・ActionHandler
・ActionHandler1
・SearchDelegateInstance
・SearchToolbarItemIdentifier
・SegmentedControlPairToolbarItemIdentifier
・SegmentedControlToolbarItemIdentifier
・TargetInstance
・ToolbarItemIdentifierAction
・ToolbarItemIdentifierQuick
・ToolbarItemIdentifierShare
・ToolbarItemIdentifierSort
・ToolbarItemIdentifierTag
- 他に、NSMakePoint/NSMakeRect/NSMakeSize(メソッド)、NSPoint/NSRect/NSSize(構造体)が必要ですが、それらはmacoslibからコピーさせて頂きました。(上記NSToolbarにコピーする。)
おわりに
メニュー対応は、アイコン向けとは別処理となって、漏れが出ないようにするのは結構メンドいです。
また、「テキストのみ」モードでのメニューは、(例えば)Finderではマウスクリックで表示されますが、今回の実装では長押し(この場合は離すとメニューが消える)、またはクリック時に素早くマウスを動かさないと、表示されません。ちなみに、macoslibのサンプルでも同様でした。
Xojoが間に一枚噛んでいることで、何らかの取りこぼしが発生している可能性もありますが、詳細は不明です。
お世話になったサイト
貴重な情報をご提供頂いている皆様に、お礼申し上げます。(以下、順不同)
参考サイト(1):Setting a Toolbar Item’s Representation
更新履歴
2021.05.11 Xojoでの実装、の30項を変更。
2020.03.09 新規作成
[Home] [MacSoft] [Donation] [History] [Privacy Policy] [Affiliate Policy]