ホームページ>開発ツール>Xojo / Real Studio Trial and Error・CocoaのDeclareでアプリアイコンを作る・フォントパネルを使う
Xojo / Real Studio Trial and Error
目次
CocoaのDeclareでアプリアイコンを作る・フォントパネルを使う
はじめに
以下は、Xojo Cocoaビルドについての話題です。
フォントパネルを表示して、選択したフォントの情報を取得する方法について、調べてみました。
なお検証には、Xojo 2022 Release 4.1を用いています。(Mac mini 2018 + macOS 15.3 Sequoia)
方針
前回アプリでは、フォント名の指定はプリセットまたは自分で調べてセットするようにしていましたが、使い勝手向上のため、フォントパネルを使えるようにしてみます。
フォントパネルの表示については、様々情報が公開されていますので、すぐに分かりました。
(NSFontPanelだけでなく、NSFontManagerと連携して使う、あたりがキモでしょうか。)
選択フォントの情報取得方法は、デリゲートや通知センターではなく、ターゲット/アクションであることも分かりました。
なので、お馴染みのObjective-CのランタイムAPIを用いた動的クラス生成を利用します。
手こずったのは、フォント取得の具体的手順で、当初はselectedFontプロパティから取れるのかと思ったらそうではなく、あらかじめフォントインスタンスを用意しておき、convertFont:で中身を入れ替える、というちょっとトリッキーなものとなっています。
参考サイト(1):objective c - NSFontManager selectedFont returning null - Stack Overflow
取得する情報は、フォント名のフルネームのみとします。サイズその他はここでは対象としません。
(注:ボールドやイタリックは、ファミリー内に存在すれば、フォント名の一部として指定することは可能。)
以上を踏まえ、(残りの)仕様は以下の通りとしました。
- Comboboxの近傍にボタンを置き、押した時に表示する。
- 非表示とのトグル動作は行わない。(消す時は、ウィンドウのクローズボタンを押す。)
- フォントパネルはクラス化する。
Xojoでの実装
注:以下の実装では、一部Xojo 2022r4.1ではDeprecatedな機能が使われています。必要なら推奨される機能に置き換えて下さい。
【ソースコードのコピー&ペーストについて】
・ソースコード(グレー背景部分の全文)をコピーし、指定のオブジェクトにペーストすると、(新規作成して名前等を個別にコピー&ペーストしなくても)復元されます。
・ペーストはオブジェクトに行って下さい。オブジェクト内のEvent Handlers/Methods/Properties等にペーストしても、うまくいかない場合があります。
・それでもペーストできない場合は、各項目のカッコ内を適用して下さい。
実行してみたところ、フォントパネルを表示し、フォント名を取得できることを確認しました。
- 前回プロジェクトをベースとする
- Window1に、DesktopBevelButton1個(Name:BevelButton1, Caption:Panel)を置く。(配置はスクリーンショット参照)
- 以下をBevelButton1にペースト(できなければ、Sub - Endの間をPressedイベントに記述)
Sub Pressed() Handles Pressed ShowFontPanel() End Sub
- 以下のWindow1のイベントを置き換え(できなければ、Sub - Endの間をOpeningイベントに記述)
Sub Opening() Handles Opening me.Top=me.Top+3 me.AcceptFileDrop("image/png") // NSFontPanel。引数は、フォントパネルクリックアクションの受け口 Dim p As NSFontPanel = new NSFontPanel(AddressOf FontSelected) // NSView。引数は順に、生成したviewのインスタンス、配置するウィンドウ、位置/サイズ Dim d As NSViewCanvas = new NSViewCanvas(pViewInst, self, NSMakeRect(10, 10, 1024, 1024)) // アイコンビュー生成(初期化) SetIconView(kReset) End Sub
- 以下をWindow1にペースト
Protected Sub FontSelected(id As Ptr, SEL As CString, sender As Ptr) 'sender = NSFontManager // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // 初めにガワを作る。 Dim sfont As Ptr = NSClassFromString("NSFont") Declare Function systemFontOfSize Lib "Cocoa" Selector "systemFontOfSize:" (receiver As Ptr, size As CGFloat) As Ptr sfont = systemFontOfSize(sfont, 12.0) // 中身はなんでも良い。 // senderにガワを渡して、選択中のフォントを取り込む Declare Function convertFont Lib "Cocoa" Selector "convertFont:" (receiver As Ptr, flg As Ptr) As Ptr sfont = convertFont(sender, sfont) // フォント名を取得 Declare Function fontName Lib "Cocoa" Selector "fontName" (receiver As Ptr) As CFStringRef Dim fname As String = fontName(sfont) // ComboBox1を書き換え ComboBox1.Text=fname End Sub
- 以下をWindow1にペースト
Protected Sub ShowFontPanel() // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Dim fmanager As Ptr = NSClassFromString("NSFontManager") Declare Function sharedFontManager Lib "Cocoa" selector "sharedFontManager" (class_id As Ptr) As Ptr fmanager = sharedFontManager(fmanager) Dim fpanel As Ptr = NSClassFromString("NSFontPanel") Declare Function sharedFontPanel Lib "Cocoa" selector "sharedFontPanel" (class_id As Ptr) As Ptr fpanel = sharedFontPanel(fpanel) Declare Sub orderFrontFontPanel Lib "Cocoa" Selector "orderFrontFontPanel:" (receiver As Ptr, val As Ptr) orderFrontFontPanel(fmanager, fpanel) End Sub
- 新規クラス(名前は、ここでは「NSFontPanel」)を作成する。
- 以下をNSFontPanelにペースト(できなければDelegatesに、名前:ActionDelegate、引数:id As Ptr, SEL As CString, sender As Ptr、を追加)
Private Sub ActionDelegate(id As Ptr, SEL As CString, sender As Ptr)
- 以下をNSFontPanelにペースト
Public Sub Constructor(action As ActionDelegate) // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr // アクションを受け取るメソッドを定義したクラスを作成。初回のみ makeTarget() // ターゲットアクションのインスタンスを作成 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 targetId As Ptr = init(alloc(TargetActionClass)) // FontManagerを取得 Dim fmanager As Ptr = NSClassFromString("NSFontManager") Declare Function sharedFontManager Lib "Cocoa" selector "sharedFontManager" (class_id As Ptr) As Ptr fmanager = sharedFontManager(fmanager) // FontManagerにターゲットアクションを設定 Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr) setTarget(fmanager, targetId) Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr) setAction(fmanager, NSSelectorFromString("action:")) // Window側でActionを受け取るメソッドを登録 ActionHandler = action End Sub
- 以下をNSFontPanelにペースト
Private Shared Sub actionEvent(id As Ptr, SEL As CString, sender As Ptr) // インスタンス側でActionを受け取るメソッドをレイズする ActionHandler.Invoke(id,SEL,sender) End Sub
- 以下をNSFontPanelにペースト
Private Shared Sub makeTarget() // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 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 TargetActionClass <> nil then return end if // クラス名をmyFontPanelAction、メタクラス名をNSObjectにして、生成 Dim newClassId As Ptr = objc_allocateClassPair(NSClassFromString("NSObject"), "myFontPanelAction", 0) // ランタイムに登録(参照を可能とするため) objc_registerClassPair newClassId // Tarrgetに送られてきたActionの受け口となるメソッドを追加(action:をXojo側で用意したactionEventメソッドで受け取る。) if not class_addMethod (newClassId, NSSelectorFromString("action:"), AddressOf actionEvent, "@@:@") then msgBox "error." return end if // クラスを保持 TargetActionClass = newClassId End Sub
- 以下をNSFontPanelにペースト(できなければShared Propertyに、Name:ActionHandler、Type:ActionDelegate、を追加)
Private Shared Property ActionHandler As ActionDelegate
- 以下をNSFontPanelにペースト(できなければShared Propertyに、Name:TargetActionClass、Type:Ptr、を追加)
Private Shared Property TargetActionClass As Ptr
おわりに
フォントパネルのインスタンスはsharedFontPanelで取得しているので、全アプリ共用なのかな?というイメージもあったため、並行して、テキストエディットでもフォントパネルを表示して、それぞれ指定してみましたが、パネル自体が別個に表示され、選択がクロスするようなこともありませんでした。
あと、時々、起動時にフォントパネルが表示されることがあります。何度かアプリの起動を繰り返すと直るので、現状、特に何もしていません。何処かに(誤って?)情報がキャッシュされているのかもしれませんが、詳細は未調査です。
お世話になったサイト
貴重な情報をご提供頂いている皆様に、お礼申し上げます。(以下、順不同)
参考サイト(1):objective c - NSFontManager selectedFont returning null - Stack Overflow
更新履歴
2025.02.17 新規作成
[Home] [MacSoft] [Donation] [History] [Privacy Policy] [Affiliate Policy]