Autodesk Inventor API Hacking (ダークテーマへの対応～アイコン編～)

# 0. はじめに
[Freeradical](https://www.freeradical.jp)の中の人、[yamarah](https://qiita.com/yamarah)です。
Inventor 2021に、プレビュー扱いですがダークテーマが実装されました。
AddInをダークテーマに対応するには、どうすれば良いかという話しです。
ダイアログを表示する場合は、その背景色を変える必要もあるでしょう。とりあえず今回は、リボンに表示されるアイコンを対応させます。

# 1. Autodesk社の対応を見る
まず、Autodesk社純正のアイコンが、どのような対応をしているのか確認しましょう。
![純正LightDark.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424028/4deadb23-91de-c120-12c1-bbc4e946fa67.png)
見ての通り、上がライトテーマで、下がダークテーマです。
単に背景の色が違うだけではなく、縁取りの線の色など、細かいデザインが変わっているのが見て取れます。
つまりは、**背景色を変えさえすれば良い、という訳ではない**ということです。

# 2. 透過アイコンを使う
とはいえ、真面目に2種類のデザインをするのは大変なので、背景色のみを変えて乗り切れないか試してみましょう。
APIを調べた限りでは、テーマ別のアイコンを設定する個所はなさそうです。ですので、背景部を透明にした透過アイコンを試してみます。
テスト用に、以下のアイコン画像を作りました。
![TestIcon1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424028/fe352874-cb46-fa12-22ee-68b63f3a0ccf.png)
アイコン作成にはジェネリック イラレと名高い[Affinity Designer](https://affinity.serif.com/ja-jp/designer/)を使いました。
Inventorがアルファチャンネルを正しく認識してくれれば、これで大丈夫なはずです。
テスト用のAddInに組み込んで、実際に表示した結果です。
![TestIcon1LightDark.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424028/145ffaaa-f6c1-e2d5-a9d6-ed574a62bb43.png)
ダークテーマにおいて、半透明部分のブレンドが正しく行われていないことが見て取れます。
どうやらInventorは、半透明部分には白をブレンドしてアルファー値を二値化処理しているようです。
つまり、この結果から導き出される結論は、アンチエイリアス処理で半透明ピクセルがあるアイコンは、ライトテーマ, ダークテーマ用に作り分けないと見栄えが悪いということです。

# 3. テーマの切り替えの検出
アイコンの差し替え自体は、`ButtonDefinition`の`StandardIcon`, `LargeIcon`を上書きすれば可能です。
問題は、どのタイミングで行うかでして、今回は`ApplicationEvents.OnApplicationOptionChange`を使います。現在のテーマは`Application.ThemeManager.ActiveTheme`で取得可能なので、`OnApplicationOptionChange`で値が変化したかどうかを調べるわけです。
抜粋したコードを以下に示します。

```C#
private string? CurrentThemeName = null;
private string? GetApplicationThemeName()
{
    return InventorApplication.ThemeManager.ActiveTheme.Name;
}

private void UpdateDefinitionIcon()
{
    if (CurrentThemeName != "DarkTheme")
    {
        ThisDefinition.StandardIcon = Properties.Resources.LightIcon16x16.ToIPictureDisp();
        ThisDefinition.LargeIcon = Properties.Resources.LightIcon32x32.ToIPictureDisp();
    }
    else
    {
        ThisDefinition.StandardIcon = Properties.Resources.DarkIcon16x16.ToIPictureDisp();
        ThisDefinition.LargeIcon = Properties.Resources.DarkIcon32x32.ToIPictureDisp();
    }
}

public void Activate(ApplicationAddInSite addInSiteObject, bool firstTime)
{
    CurrentThemeName = GetApplicationThemeName();
    UpdateDefinitionIcon();
    InventorApplication.ApplicationEvents.OnApplicationOptionChange += ApplicationEvents_OnApplicationOptionChange;
}


private void ApplicationEvents_OnApplicationOptionChange(EventTimingEnum BeforeOrAfter, NameValueMap Context, out HandlingCodeEnum HandlingCode)
{
    if (BeforeOrAfter == EventTimingEnum.kAfter)
    {
        var themeName = GetApplicationThemeName();
        if (CurrentThemeName != themeName)
        {
            // Themeが変わった
            CurrentThemeName = themeName;
            UpdateDefinitionIcon();
        }
    }
    HandlingCode = HandlingCodeEnum.kEventNotHandled;
}

public void Deactivate()
{
    InventorApplication.ApplicationEvents.OnApplicationOptionChange -= ApplicationEvents_OnApplicationOptionChange;
}
```
これでテーマが変わるタイミングで、アイコンが差し替えられるはずです。

# 4. 背景焼きこみアイコンの差し替え
背景をライト、ダーク背景色にして、アンチエイリアスを焼き込こんだアイコンを作って、うまく切り替わるか試してみました。
![TestIcon2LightDark.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424028/49096f05-7dc2-4a8f-9015-1df3bb5656cf.png)
良い感じに馴染んでいます。これにて解決・・・となるはずだったのですが・・・

# 5. ハイライト時に背景色が変わらない
ボタンにマウスポインタがフライオーバーした時に、
![TestIcon2Bad.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424028/7b2e67c8-a364-b4fb-05c4-3b49cc302ec6.png)
何やら違和感が。ダークテーマの背景をアイコンに焼き込んだので、うまくハイライトせずに四角い枠が見えてしまっています。
ここまで来れば意地で、アイコン画像をジェネリック フォトショこと[Affinity Photo](https://affinity.serif.com/ja-jp/photo/)に取り込み、背景を透明にします。
![Dark切り抜き.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424028/47e3d0a5-e928-f2ff-1766-1785fa737d6b.png)
アンチエイリアス部のみ焼き込みを残して、他の背景は100%透過にしたわけです。
このアイコン画像と差し替えて再チャレンジ。
![TestIcon3OK.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424028/16ce4191-f840-7258-6876-356d37dc15aa.png)
今度はうまく行きました。

# 6. 旧バージョンへの対応
`Theme`オブジェクトはInventor 2021で導入されたものであり、バージョン25以降のInteropを使わないとアクセスできません。しかし、そうすると、作成されたAddInはInventor2020以前では動作しなくなります。
悩ましい問題ですが、ここは泥臭く`dynamic`を使って逃げましょう。
先のコードの`GetApplicationThemeName()`を、以下の通り変更します。

```c#
private string? GetApplicationThemeName()
{
    try
    {
        return ((dynamic)InventorApplication).ThemeManager.ActiveTheme.Name;
    }
    catch
    {
        return null;
    }
}
```
例外が発生すれば`null`を返し、結果としてライトテーマのアイコンが設定されます。
`dynamic`並びに例外処理は重たい処理なので避けたいということならば、`Application.SoftwareVersion.Major`で切り分けでも良いですし、例外検知でフラグを立てて2回目以降は常にnullを返しても良いと思います。

# 99. 親の記事に戻る
[Autodesk Inventor API Hacking (概略)](https://qiita.com/yamarah/items/cee08eb8f2b7aecbda83)