[Delphi] [FMX] クリップボードの使い方

# Clipboard

つい先日、Clipboard の記事を見かけて、そういえば Delphi で Clipboard 使うのは簡単すぎて Qiita に書いてないなあと、そして、もしかしたら Berlin から登場している新しい Clipboard サービスを知らない方も居そう！と思って書いてみることに。

※この記事は FireMonkey 向けです。VCL はまた別の方法になります。

# IFMXClipboardService

Delphi FireMonkey で Clipboard といえば IFMXClipboardService !
ということで、これを使うと

```pascal
  var Clipboard: IFMXClipboardService;
  if
    not TPlatformServices.Current.SupportsPlatformService(
      IFMXClipboardService,
      Clipboard)
  then
    Exit;

  // 値の読み出し T は任意の型
  var X := Clipboard.GetClipboard.AsType<T>;

  // 値の設定 X は任意の変数
  Clipboard.SetClipboard(X);
```

こんな感じになります。
めっちゃ簡単。

GetClipboad / SetClipboard はどちらも [TValue 型](http://docwiki.embarcadero.com/Libraries/Sydney/ja/System.Rtti.TValue)の値を読み書きするので、どんな型でも入れられますが、Clipboard が対応している形式ではない場合は意味がありません。

特に Android は特殊で Text, Uri オブジェクト, Intent オブジェクトのみ扱えます。
詳しくは↓こちらを

■**Android デベロッパー**
[コピー＆ペースト](https://developer.android.com/guide/topics/text/copy-paste?hl=ja)

Android の場合は、単機能のアプリを Intent を介して順々に実行するスタイルの OS なので、Text, Uri, Intent のみがサポートされているのは頷けますね。

それ以外のプラットフォームでは Text, Image がデフォルトでサポートされています。
つまり、Text であれば、Windows, macOS, iOS, Android, Linux でクリップボードが使えるということです。
Image に限ると Windows, macOS, iOS, Linux で使えます。

## Image の読み出し

先ほどの T の部分を [FMX.Surface.TBitmapSurface](http://docwiki.embarcadero.com/Libraries/Sydney/ja/FMX.Surfaces.TBitmapSurface) にして、画像を読み出してみます。

```pascal
  var X := Clipboard.GetClipboard.AsType<TBitmapSurface>;
  if X <> nil then
    Image1.Bitmap.Assign(X);
```

Linux (Ubuntu 18.04 + VcXsrv) で上記のコードを使って読み出したところ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12977/d0845bb7-be30-ae02-334d-a57e2a3b2432.png)

ただ、このコードでは Clipboard 内に T 型のデータ（ここでは TBitmapSurface のデータ）が無い場合エラーになってしまいます。
事前に、Clipboard 内に何が入っているのか判ったらいいですよね。

# IFMXExtendedClipboardService

Berlin から登場した新しい Clipboard Service の [IFMXExtendedClipboardService](http://docwiki.embarcadero.com/Libraries/Sydney/ja/FMX.Clipboard.IFMXExtendedClipboardService) を使うと、Clipboard 内のデータがどんな型なのか事前に把握できます。

まずはコードを見てください。

```pascal
  var Clipboard: IFMXExtendedClipboardService;
  if
    not TPlatformServices.Current.SupportsPlatformService(
      IFMXExtendedClipboardService,
      Clipboard)
  then
    Exit;

  if Clipboard.HasText then // Text が格納されている？
    Edit1.Text := Clipboard.GetText; // Clipboard 内を文字列として取り出す

  if Clipboard.HasImage then // Image が格納されている？
    Image1.Bitmap.Assign(Clipboard.GetImage); // Clipboard 内を TBitmapSurface として取り出す
```

このように HasXXX で、Clipboard に XXX 型のデータがあるか？が判別でき、さらに GetXXX で XXX 型のデータを取り出せます。

では、用意されている XXX は何があるかというと

* Text
* Image (ただし上述のように Android では常に False, nil を返す)
* CustomFormat

となります。

ここで、CustomFormat は任意のデータを Clipboard 
扱う仕組みです…が、 Format を [RegisterCustomFormat](http://docwiki.embarcadero.com/Libraries/Sydney/ja/FMX.Clipboard.IFMXExtendedClipboardService.RegisterCustomFormat) を使って事前に登録するので、同じアプリケーション間くらいでしか使い道は無さそうです。

なお、IFMXExtendedClipboardService は IFMXClipboardService を継承しているので、データを登録する方法は IMFXClipboardService と変わらず 

```pascal
  SetClipboard(X);
```

を使います。

# 最後に

ということで、最後に Desktop 系 OS で Clipboard から画像を取り出したスクリーンショットを貼っておきます（せっかくなので全部違うスタイルで色とりどりに。Steller と Ubuntu ClearFantsy は [DelphiStyles.com の紹介ページ](https://www.delphistyles.com/fmx/index.html)はありませんでした）。

設計時画面とソースコードは以下の通り

##### 設計とソース
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12977/d8ec9a4d-bbed-9cd8-97bb-b7ce4af2b227.png)


```pascal:unit1.pas
unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
  FMX.Controls.Presentation, FMX.Edit, FMX.Objects, FMX.Layouts;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Edit1: TEdit;
    Panel1: TPanel;
    Button1: TButton;
    Layout1: TLayout;
    StyleBook1: TStyleBook;
    procedure Button1Click(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses
  FMX.Clipboard, FMX.Platform;

procedure TForm1.Button1Click(Sender: TObject);
begin
  var Clipboard: IFMXExtendedClipboardService;
  if
    not TPlatformServices.Current.SupportsPlatformService(
      IFMXExtendedClipboardService,
      Clipboard)
  then
    Exit;

  if Clipboard.HasText then
    Edit1.Text := Clipboard.GetText;

  if Clipboard.HasImage then
    Image1.Bitmap.Assign(Clipboard.GetImage);
end;

end.
```

##### Screenshot ギャラリー

**Windows (Style: [ClearCerulean](https://www.delphistyles.com/fmx/ClearGulfStream.html))**

<img width="416" alt="Windows" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12977/bece2718-d28c-35d4-f935-f0050ffdb836.png">

**macOS (Style: Steller)**

<img width="416" alt="macOS" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12977/a51524aa-aef1-0934-eff2-58c3f2a42799.png">

**Ubuntu 18.04 + VcXsrv (Style: Ubuntu ClearFantasy)**

<img width="416" alt="Linux" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12977/4c2be84c-208e-5107-dfbe-cc5f23a51319.png">

Delphi を使うと簡単に Windows, macOS, iOS, Android, Linux で Clipboard が使えますね！

ちなみに、ここに使った画像は 2020/8 現在はまっている [SPACE ENGINE](http://spaceengine.org/) のスクリーンショットです！