クリスマスプレゼント（ねこのデスクトップアクセサリ）

# ねこです
クソアプリアドベントカレンダーの「[猫に仕事を邪魔されてみたかったので技術で実現した](https://qiita.com/ampersand/items/7180f657b3d0a0c05734)」が話題ですね。
可愛い猫[^1]がブラウザ中を駆け回る…！かわいい！

そんな訳で僕も作りたくなったので、Delphi で作ってみました。
ちなみに、Windows, macOS の両方で動きます。

実行ファイル：https://bitbucket.org/freeonterminate/nekodesu/downloads/
ソース一式：https://bitbucket.org/freeonterminate/nekodesu/
完全無料の [Delphi Community Edition](https://www.embarcadero.com/jp/products/delphi/starter) でもビルド出来ると思うので是非試して見てください。

# 起動
起動すると可愛い猫ちゃんが画面を右から左に駆け抜けていきます。
タスクトレイ上にアイコンも追加されます。
![neko.gif](https://qiita-image-store.s3.amazonaws.com/0/12977/73b347ce-b228-6ca3-0ec3-85d752fec01b.gif)

# 終了
終了時はタスクトレイ上のポップアップメニューで「終了」を選びます。
終了させると絶望から身体の色が変化するようですね。
![neko4.gif](https://qiita-image-store.s3.amazonaws.com/0/12977/b8b68c11-a6e1-a7bc-fb52-5f34cc7058d9.gif)

# ドラッグ
ドラッグすると身体を赤から青に発光させて不満を表しているみたい。
![neko4.gif](https://qiita-image-store.s3.amazonaws.com/0/12977/f7176ffe-2a93-ba79-0ba0-9acf94344e44.gif)

# 解説
デスクトップアクセサリを作るテクニックとして「[FireMonkey で Window Drag](https://qiita.com/pik/items/907641319e4de4d74805)」と「[Delphi + FireMonkey で TaskTray / StatusBar にアイコンとメニューを実装する](https://qiita.com/pik/items/bd3448e2cf0c4c528027)」を以前に解説しました。

今回はその２つを組み合わせて実際に可愛い猫ちゃんのデスクトップアクセサリを作りました。

### アニメーション
FireMonkey のアニメーションはノーコーディングで実装出来るため非常に楽ちんです。
ですが、今回の猫ちゃんのアニメーションでは FireMonkey の仕組みを使いませんでした。

#### FMX アニメーションの仕組み
FireMonkey の [Animation](http://docwiki.embarcadero.com/Libraries/Rio/ja/FMX.Ani.TAnimation) コンポーネントは構造ツリー上の親のプロパティ値を変更することでアニメーションさせています。
そのため Animation コンポーネントは親のプロパティを自動的に認識します。
たとえば、[TFloatAnimation](http://docwiki.embarcadero.com/Libraries/Rio/ja/FMX.Ani.TFloatAnimation) は浮動小数点型のプロパティを、[TColorAnimation](http://docwiki.embarcadero.com/Libraries/Rio/ja/FMX.Ani.TColorAnimation) は [TAlphaColor](http://docwiki.embarcadero.com/Libraries/Rio/ja/System.UITypes.TAlphaColor) 型のプロパティを、といった具合です。

![animation.png](https://qiita-image-store.s3.amazonaws.com/0/12977/4f4317d7-7b18-e654-a456-2e482171bf9e.png)

#### BitmapListAnimation
当初は [TBitmapListAnimation](http://docwiki.embarcadero.com/Libraries/Rio/ja/FMX.Ani.TBitmapListAnimation) を使おうと思っていました。
TBitmapListAnimation は１枚の絵からアニメーションを作り出してくれる便利なコンポーネントです。
下は DocWiki から持ってきた図ですが、このように同じサイズの画像を１枚にしたものからアニメーションを作成してくれます。
![Sprint.png](https://qiita-image-store.s3.amazonaws.com/0/12977/08d74552-bdf2-d06b-bab1-70dfc88afe6f.png)

TBitmapListAnimation が認識するのは [TBitmap](http://docwiki.embarcadero.com/Libraries/Rio/ja/FMX.Graphics.TBitmap) 型のプロパティで、 TBitmap 型のプロパティを持つ [TImageControl](http://docwiki.embarcadero.com/Libraries/Rio/ja/FMX.StdCtrls.TImageControl) と組み合わせて使うのが一般的なようです。
しかし、TImageControl はデフォルト（Style を作らないと）だと背景があるため Form に透過 PNG を適用できません。
一方、Form に透過 PNG を適用して Form の形を変更してくれる TImage の Bitmap プロパティは Published になっておらず、TBitmapListAnimation からは存在が見えません。
そのため、今回は TBitmapListAnimation を使うのを諦め、[TTimer](http://docwiki.embarcadero.com/Libraries/Rio/ja/FMX.Types.TTimer) を使う方法にしました。


#### ImageList によるアニメーション
下記の３枚の画像を TImageList に保存して、時間が来たら自分で画像を取り出して TImage に割り当てています。
![nekos.png](https://qiita-image-store.s3.amazonaws.com/0/12977/857c7cbe-1a1e-83c2-5192-858a5137a1bd.png)
![imglst.png](https://qiita-image-store.s3.amazonaws.com/0/12977/05c56937-a173-1a37-79d1-01ed0da1473a.png)

TImageList からビットマップを取り出す具体的な方法は以下の通りです。

```pascal
// Bmp の解放は ImageList がやるので解放するとエラーになる！
var Bmp := 
  ImageList.Bitmap(
    TSizeF.Create(240, 360), // 取り出すビットマップのサイズ
    0 // ImageList の何番目の画像かを指定（アニメーションさせる場合はここを変更していく）
  );

Image1.Bitmap.Assign(Bmp); // TImage の Bitmap は public では存在する
```

この処理を周期的に呼べば、アニメーションされます。

#### ColorAnimation
今回ドラッグしている時にねこちゃんの周りを光らせようと思って、GlowEffect の下に TColorAnimation を付けました。
![color.png](https://qiita-image-store.s3.amazonaws.com/0/12977/f9d9c729-7706-c21f-1378-d8fda5b1bd3f.png)
ColorAnimation は設定した GlowColor を１秒（Duration）かけて赤（StartValue）から青（StopValue）に変更します。
また AutoReverse プロパティを True にしたので、次の１秒では逆に青から赤に変更します。
さらに、Loop プロパティが True なので、このアニメーションを何度も繰り返します。

### エフェクト
終了時のエフェクトには少しだけテクニックを使いました。
エフェクトは [TInvertEffect](http://docwiki.embarcadero.com/Libraries/Rio/ja/FMX.Filter.Effects.TInvertEffect) と [TGlowEffecct](http://docwiki.embarcadero.com/Libraries/Rio/ja/FMX.Effects.TGlowEffect) の２つを掛けています。
ですが、FireMonkey のエフェクトは１つのコントロールにつき１つしか適用できません。
しかし、[TLayout](http://docwiki.embarcadero.com/Libraries/Rio/ja/FMX.Layouts.TLayout) を上手く使うとエフェクトを多重に掛けられるようになります。
エフェクトが必要な数だけ TLayout を作り、多重の親子構造にしてしまう、という方法です。

下図は Layout の５段重ね＋Image で、６個のエフェクトを同時に掛けた例です。

![effect2.png](https://qiita-image-store.s3.amazonaws.com/0/12977/f3e46888-c0cb-1986-0568-3ceb2ce120e7.png)

ということで、ここでは GlowEffect を Layout に、InvertEffect を Image に適用しています。
その様子が下図です。

![layout.png](https://qiita-image-store.s3.amazonaws.com/0/12977/9ea148b3-64a6-d3da-3dc4-40a6d7c6c76b.png)

これを適用するとこんな感じで、発光しつつ色が反転します。

![nekoend.png](https://qiita-image-store.s3.amazonaws.com/0/12977/587ec474-94f1-2656-bf90-3367fd30551c.png)

# マルチプラットフォーム

Delphi なので、何も変更せずに macOS でも動作します。

![mac.png](https://qiita-image-store.s3.amazonaws.com/0/12977/4ca330ac-86f5-693b-fc94-34c0666d2057.png)

# ライブラリ
TrayIcon ライブラリは「[Delphi + FireMonkey で TaskTray / StatusBar にアイコンとメニューを実装する](https://qiita.com/pik/items/bd3448e2cf0c4c528027)」で既に紹介しました。
リポジトリの Lib/Utils ディレクトリにいくつかライブラリが入っています。
この中で、PK.Utils.Application を紹介します。

### PK.Utils.Application
このライブラリは TApplication に機能を追加する [Class Helper](http://docwiki.embarcadero.com/RADStudio/Rio/ja/%E3%82%AF%E3%83%A9%E3%82%B9_%E3%83%98%E3%83%AB%E3%83%91%E3%81%A8%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89_%E3%83%98%E3%83%AB%E3%83%91%EF%BC%88Delphi%EF%BC%89) です。
以下の５つのメソッド・プロパティを追加します。

```pascal
procedure ShowTaskBar;
procedure HideTaskBar;
function Path: String; // Path Only
function ExeName: String; // Full Path with FileName
property Version: String read GetVersion;
```

ShowTaskBar / HideTaskBar は Windows であればタスクバーに表示されるアプリケーションのバー、macOS であればドックに表示されるアイコンの表示と非表示を制御します。
今回のようなデスクトップアクセサリではタスクバーにアプリケーションが表示されていると邪魔なので、このメソッドで表示を消しています。

ExeName メソッドは [VCL の TApplication](http://docwiki.embarcadero.com/Libraries/Rio/ja/Vcl.Forms.TApplication) にあった [ExeName](http://docwiki.embarcadero.com/Libraries/Rio/ja/Vcl.Forms.TApplication.ExeName) を [FMX の TApplication](http://docwiki.embarcadero.com/Libraries/Rio/ja/FMX.Forms.TApplication) に付加する物です。
この ExeName から Path だけを取り出すために Path メソッドもあります。
macOS のアプリケーションは .app という拡張子をもつディレクトリになっているため、その直前までのパスを取り出すのが少し面倒ですが、このメソッドで簡単に取り出せます。

最後に Version ですが、実行ファイルに結びついた Version 情報を Windows / macOS で統一的に扱えるようにしています。

バージョン画面で表示されるのは、この Version プロパティを使った値です。
![version.png](https://qiita-image-store.s3.amazonaws.com/0/12977/5329dd09-e856-d2e1-d952-d6c27ff1f48d.png)

# まとめ
か、かわ、ｋわいい、ねこちゃん、かわいｉね.:∴∵tanasinn∵∴∵∴

[^1]: 出典：http://ja.scp-wiki.net/scp-040-jp, CC BY-SA 3.0