Surface Dialが出たころはWindows 10 Anniversary Update (Build 14393)だったのが、今はCreators Update (Build 15063)で、RadialController周りも少しアップデートがあったことに今更気づいたのでC++から触ってみます。
追加されたインターフェース
RadialController周りで増えたのがこのあたり。
- IRadialController2
- IRadialControllerMenuItemStatics2
- IRadialControllerRotationChangedEventArgs2
- IRadialControllerScreenContactContinuedEventArgs2
- IRadialControllerScreenContactStartedEventArgs2
このうちEventArgsとついているのは、これまで通りのイベント引数に対してQueryInterface
すると読めます。
// たとえばIRadialControllerRotationChangedEventArgs2の場合 /* using namespace Microsoft::WRL; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::UI::Input; */ EventRegistrationToken rotationChangedEvent; // ComPtr<IRadialController _controller->add_RotationChanged( Callback<ITypedEventHandler<RadialController*, RadialControllerRotationChangedEventArgs*>>( [this](IRadialController*, IRadialControllerRotationChangedEventArgs* args) { ComPtr<IRadialControllerRotationEventArgs2> args2; args->QueryInterface(IID_PPV_ARGS(&args)); // 好きに使う // args2->get_SimpleHapticsController(); return S_OK; }).Get(), &rotationChangedEvent);
残りのIRadialController2
とIRadialControllerMenuItemStatics2
は、IRadialController
とIRadialControllerMenuItemStatics
に対して
QueryInterface
すれば読めます。
/* using namespace Microsoft::WRL; using namespace Windows::Foundation; using namespace ABI::Windows::UI::Input; */ ComPtr<IRadialControllerMenuItemStatics> menuItemStatics; ComPtr<IRadialControllerMenuItemStatics2> menuItemStatics2; GetActivationFactory( HStringReference(RuntimeClass_Windows_UI_Input_RadialControllerMenuItem).Get(), &menuItemStatics); menuItemStatics->QueryInterface(IID_PPV_ARGS(&menuItemStatics2));
だいたいQueryInterface
すればどうにかなります。
C++/Win32でDialを使ったコードを書く
前に書いたやつを書き直してGitHubにサンプルプロジェクトを公開しました。VS2017+15063 SDKでそのまま実行できるはずです。
IVector実装の入手
システムアイコンを追加削除するには、任意のIVector実装が必要です。 正直実装するのは面倒なのでWin2Dで使用されている実装を拝借します。 この実装はMITライセンスで公開されています。
Win2Dから
- ErrorHandling.h
- LifeSpanTracker.h
- Vector.h
- WinStringBuilder.h
- WinStringWrapper.h
を入手してプロジェクトに追加します。
プロジェクトの設定
プロジェクトの設定の変更が必要です。プロジェクトを右クリックした、プロパティから次の設定を変更します。
15063になって、WindowsSDK_UnionMetadataPath
が使えるようになったのでちょっとだけ短くなりました。
- 構成プロパティ > 全般 > Windows SDK バージョン を “10.0.15063.0” に設定
- 構成プロパティ > C/C++ > 全般 > Windows ランタイム拡張機能の使用 を “はい (/ZW)” に設定
- 構成プロパティ > C/C++ > 全般 > 追加の #using ディレクトリを “$(VC_ReferencesPath_VC_x86)\store\references;$(WindowsSDK_UnionMetadataPath)” に設定
- 構成プロパティ > C/C++ > コード生成 > 最小リビルドを有効にするを “いいえ (/Gm-)” に設定
- 構成プロパティ > MIDL > 全般 > Windows ランタイムを有効にしますを “はい (/winrt)” に設定
- 構成プロパティ > MIDL > 全般 > 追加のメタデータ ディレクトリを “$(WindowsSDK_UnionMetadataPath)” に設定
- 構成プロパティ > MIDL > 詳細設定 > ‘ABI’ 名前空間を追加するを “はい (/ns_prefix)” に設定
IDLの作成
すごく面倒なんですが、IVector<T>
をインスタンス化するにはIDLが必要です。
genericとC++のテンプレートと、COMの都合だそうです*1
プロジェクトを右クリックして、追加、コード、MIDL ファイルで追加します。
ここではとりあえずVector.idl
としておきます。
今回はWindows.Foundation.Collections.IVector<Windows.UI.Input.RadialControllerSystemMenuItemKind>
が使えればいいので、
こんな感じで書いておきます。namespaceで指定する名前空間はなんでもいいみたいです*2。
import "inspectable.idl"; import "Windows.Foundation.idl"; import "Windows.UI.Input.idl"; namespace collections { declare{ interface Windows.Foundation.Collections.IVector<Windows.UI.Input.RadialControllerSystemMenuItemKind>; } }
Vector_h.hの参照
だいたい終わりです。
IVector<T>
のインスタンスを作成するMicrosoft::WRL::Make<T>
を読んでいるファイルで、
Vector_h.h
をインクルードする必要があります。
このときのファイル名はIDLのファイル名から拡張子を除いた部分に_hを付けて拡張子idlをhに変更したものを 指定します。
#include"Vector_h.h"
おしまい
これでとりあえずRadialControllerの全機能が使えると思います。 IDL周り以外は大して難しくないはずなので遊んでみてください。