ホームページ開発ツール>Xojo / Real Studio Trial and Error・CocoaのDeclareでツールバーを実装する・アイコンをカスタマイズする

 Xojo / Real Studio Trial and Error

CocoaのDeclareでツールバーを実装する・アイコンをカスタマイズする

目次
 はじめに

 以下は、Xojo Cocoaビルドについての話題です。

 NSToolbarのBig Sur対応のうち、前回からの積み残しとなっていた、ツールバーアイテムアイコンのカスタマイズについて進展がありましたので、ここに纏めておきます。

 なお検証には、Xojo 2021 Release 1.1を用いています。(Mac mini 2018 + macOS 11.5.1 Big Sur)


 方針

 SF Symbolsのカスタマイズと参照方法については、以下のサイトを参考にさせて頂きました。

 参考サイト(1):SF Symbolsの使い方とカスタマイズの仕方 - くらげだらけ

 基本的な流れは記述されているその通りですが、補足点は以下の通りです。
 一つ目はシンボルの編集ですが、上記サイトではカスタマイズにAdobe Illustratorを使われていますが、フリーソフトのInkscapeでも可能なことが確認できましたので、これを使用しています。
注:ググると、MacではXQuartzが必要、との情報がヒットしますが、これは0.92以前の話で、1.0以降では不要となっています。
 二つ目にシンボルの利用方法ですが、意外にも?、XojoでもAssets.carファイルを参照できることが確認できました。
(アプリケーションパッケージのResourcesフォルダー内に置いておけば、NSImageのimageNamed:で呼び出せる。)
 なので、Assets.carファイル作成には上記サイトの通りXcodeを使い、Xojoプロジェクトでの利用部分のみ独自に対応することになります。

 なお、今回はルートの確保が目的なので、一から作るのではなく、既存のシンボルを小改造することとします。


 シンボルのカスタマイズ

 まずは、SF Symbols.appでシンボルを書き出します。
  1. ベースとして、(ここでは)rectangle.topthird.insetを使う。
  2. rectangle.topthird.insetを選択し、ファイル>カスタム・シンボル・テンプレートを書き出す...でsvgファイルに書き出す。
 次に、Inkscapeでシンボルを編集します。
  1. svgファイルをInkscapeで開く。
    S Shot1
    (クリックで拡大)
  2. 実験で確認したところ、ツールバーのモードが「アイコンとテキスト」ではRegular - Medium、「アイコンのみ」ではMedium - Largeが使われていたので、一先ずこの二つを対象とする。(勿論、最終的には全て行った方がいいでしょう。)
  3. 四角の中の横棒を中央に移動する。
    S Shot1
    (クリックで拡大。注:赤丸は目印として後付けたもので、データには含まれない。)
  4. 名前を付けて保存...で保存する。(名前はここでは、rectangle.middlethird.inset.svg
 引き続き、XcodeでAssets.carファイルを作成します。(追記:Xcodeを使わない方法を確認できました。おわりに参照。)
  1. Xcodeを起動し、macOS>Application>Appを選択してプロジェクトを作成。
    (注:このプロジェクトはAssets.carファイル作成のためだけのもので、コードは書きません。)
  2. 左ペインのAssets.xcassetsをクリックし、rectangle.topthird.inset.svgrectangle.middlethird.inset.svgを中央ペインにドラッグ&ドロップする。
    S Shot1
    (クリックで拡大)
  3. ビルドする。
  4. ビルドされたアプリを、右クリック>パッケージの内容を表示、で開き、Resourcesフォルダー内のAssets.carファイルをコピーし、任意の場所(ここでは、以下で作成するXojoプロジェクトと同じフォルダー内)にペーストする。

 Xojoでの実装
【ソースコードのコピー&ペーストについて】
・ソースコード(グレー背景部分の全文)をコピーし、指定のオブジェクトにペーストすると、(新規作成して名前等を個別にコピー&ペーストしなくても)復元されます。
・ペーストはオブジェクトに行って下さい。オブジェクト内のEvent Handlers/Methods/Properties等にペーストしても、うまくいかない場合があります。
・それでもペーストできない場合は、各項目のカッコ内を適用して下さい。
  1. 前回プロジェクトをベースとする
  2. Assets.carファイルをXojoプロジェクト左ペインにドラッグ&ドロップ
  3. CocoaToolBarのsetToolItemSCbutton1メソッドを以下に差し替え
    Private Shared Function setToolItemSCbutton1(itemIdentifier As String, label As String, labelL As String, tooltip As String, icon As String, extn 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
      
      // アイコン画像の取得(SF Symbols。>Resources>Assets.carから取得)
      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,40,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, 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 setTag Lib "Cocoa" Selector "setTag:forSegment:" (receiver As Ptr, tag As Integer, no As Integer)
      setTag(cell(segment1), tag, 0)  // タグだけ判別するので、同じActionメソッドを使用するセグメント間で固有の数値にする
      
      // アイコンのLargeSizeを有効に(NSControlSizeLarge = 3)
      Declare Sub setControlSize Lib "AppKit" Selector "setControlSize:" (receiver As Ptr, cnt As Integer)
      setControlSize(segment1, 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, labelL)
      Declare Sub setPaletteLabel Lib "Cocoa" Selector "setPaletteLabel:" (receiver As Ptr, label As CFStringRef)
      setPaletteLabel(toolbarItem, label)
      Declare Sub setToolTip Lib "Cocoa" Selector "setToolTip:" (receiver As Ptr, text As CFStringRef)
      setToolTip(toolbarItem, tooltip)
      
      // アイテムのビューにセグメンテッドコントロールを設定
      Declare Sub setView Lib "Cocoa" Selector "setView:" (receiver As Ptr, actionTarget As Ptr)
      setView(toolbarItem, segment1)
      
      // Target/Action設定
      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:"))
      
      // Hover Effectを有効に
      Declare Sub setBordered Lib "AppKit" Selector "setBordered:" (receiver As Ptr, flg As Boolean)
      setBordered(toolbarItem, true)
      
      // clean up
      Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr)
      release(segment1)
      
      // 生成したアイテムを返す
      return toolbarItem
    End Function
    
  4. CocoaToolBarのtoolbarItemForItemIdentifierWillBeInsertedIntoToolbarメソッドを以下に差し替え
    Private Shared Function toolbarItemForItemIdentifierWillBeInsertedIntoToolbar(id as Ptr, sel as Ptr, toolbar As Ptr, itemIdentifier As CFStringRef, flag As Boolean) As Ptr
      if itemIdentifier = NewToolbarItemIdentifier then
        return setToolItemSCbutton1(itemIdentifier,"新規","新規","新規に作成します","rectangle.topthird.inset","",601)  // 注:SF Symbolsでは拡張子は不要だが、これは従来の名残り。
        
      elseif itemIdentifier = SaveToolbarItemIdentifier then
        return setToolItemSCbutton1(itemIdentifier,"保存","保存","保存します","rectangle.middlethird.inset","",602)
        
      elseif itemIdentifier = SearchToolbarItemIdentifier then  // 検索
        return setToolItemSearch(itemIdentifier)
        
      elseif itemIdentifier = "TrackingSeparatorToolbarItemIdentifier" then  // セパレーター
        return setToolItemTrackSep()
        
      else
        return nil
      end if
    End Function
    
 実行してみたところ、カスタマイズしたアイコンが表示されることを確認しました。
S Shot 4

 おわりに

 上記例では、カスタマイズしたものだけでなく、オリジナルも指定していますが、同様に表示できています。
 このことから、SF Symbolsはシステムに依存せずに自前で用意する、というアプローチもありそうです。

 Assets.carファイルの作成フェイズは冗長ですが、Xojoだけで対応できるのかは不明です。
 また、例えばコマンドラインでsvgからAssets.carファイルが作成できれば、もう少し簡略化できるかもしれませんが、その辺りも未調査です。
追記1:コマンドラインでsvgからAssets.carファイルを作成できることが確認できました。なので、簡単なツールを作ってみました。詳細はこちら
 参考サイト(2):How Can I create Assets.car using … | Apple Developer Forums

追記2:imageNamed:によるsvgファイルの取得時に、ファイル名によっては正しく処理されない場合があることが確認できています。
例えば、square.and.arrow.down.fillは、square.and.arrow.downと認識されます。
少し実験してみましたが、(1)ドットの数が多すぎる、fillを認識しない、ということではない、(2)fillを別の文字列に変えるのは無効だがdownを変えるのは有効、といったことが見えてきたものの、規則性を定かにはできていません。
個別に目視確認後に適宜名前を変えるか、問題の起きないネーミングルールを見つけて統一する等、何かしら対策する必要がありそうです。

 お世話になったサイト

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

 参考サイト(1):SF Symbolsの使い方とカスタマイズの仕方 - くらげだらけ
 参考サイト(2):How Can I create Assets.car using … | Apple Developer Forums


 更新履歴

 2021.08.17 シンボルのカスタマイズ、に追記を追加。おわりに、に追記1と追記2を追加。参考サイト(2)を追加。
 2021.08.06 新規作成


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