Sketch Plugin についての要約とメモ。あるいは Sketch Plugin の始め方。

これは、 Sketch 3 のプラグインに未来を見た男の物語である。つまり [Sketch Developer](http://bohemiancoding.com/sketch/support/developer/) の要約とメモ。

## プラグインで何ができるのか

- 複雑なルールに基づいて、ドキュメント内のものを選択する
- レイヤーのプロパティを操作する
- 新しいレイヤーを作成する
- サポートされている全てのフォーマットでアセットを出力する
- ユーザからの入力を受け付けたり、ユーザへ示すことができる
- 外部ファイルやウェブサービスからデータを取得することができる
- クリップボードを使うことができる
- Sketch の環境を操作することができる (ガイド、ズームなど)
- プラグインからメニューを呼び出すことにより、既存の機能を自動化する

ちなみに例えば、こんなプラグインがある

- [design specs](https://github.com/utom/sketch-measure)
- [mobile prototyping](https://github.com/bomberstudios/sketch-framer)
- [code generation for Android apps](https://github.com/malkomalko/sketch-android-kit)


## イントロダクション

### プラグインの動かしかた

インストールする方法で変わるので、 `Sketch > Plugins > Reveal Plugins Folder` からパスを確認する。基本的にここに置いておけば自動認識して `Plugins` の中に表示される


### JSTalk

SketchPlguin を記述する言語は JSTalk です。 JavaScript と Obj-C を混ぜて2で割って全部捨てた感じがする。

拡張子は、プラグイン本体は `.sketchplugin`。外部ファイルとして `.jstalk`, `.js` が使える。

##### 注意

- 3.0.2 で YES, NO が消えた

    何か元々 true, false の alias だったらしいですがそれが消えました。なので、サンプルコードとかで YES, NO が使われてても使えません。もしくは自分で alias 貼ってください。


### 標準変数

Sketch Plugin からアクセスできるグローバルな標準変数

- `doc` : `MSDocument` のインスタンス。現在のドキュメントへの参照を持つ。
- `selection` : `NSArray` のインスタンス。現在選択しているレイヤーたち
- `scriptPath` : `NSString` のインスタンス。現在のプラグインへのパス。


### Cocoa へのアクセス

JSTalk スクリプトから Cocoa API へアクセスできる。以下は、 File Picker panel を開く例である。

```jstalk
// NSOpenPanel クラスを使う
var openPanel = [NSOpenPanel openPanel]

[openPanel setCanChooseDirectories:false]
[openPanel setCanChooseFiles:true]
[openPanel setCanCreateDirectories:false]
[openPanel setDirectoryURL:[NSURL fileURLWithPath:"~/Documents/"]]

[openPanel setTitle:@"Choose a file"]
[openPanel setPrompt:@"Choose"]
[openPanel runModal]
```


### デバッグ

基本的に以下のようなコードを埋め込むと、 system.log へ出力してくれる。らしい。

```javascript
log(SOME_VALUE);
```

- Console.app で拾う ← まちがい
- tail で拾う ← せいかい

    ```
    $ tail -f /var/log/system.log|grep Sketch
    ```


##### おまけ

何か適当に alert 出したりしたかったら

- alert, [doc showMessage], Cocoa dialogs 何かが使える
- もしくは [Sam Deane’s log panel](https://github.com/samdeane/sketch-scripts/blob/master/Log%20Example.sketchplugin) こういう感じのテクを使う



## 基本情報


### Plugin をまたいだ共通のコードの作り方

```
#import 'path/to/file'
```

のように、 `#import` 文が使えるので、それで共通のファイルを読み込む。

- 読み込むファイルは jstalk, js のどちらでも読める
- `#import` 文は必ずシングルクォートで囲む
- インポートされたファイル内の検索パスは、現在のフォルダ, プラグインのルートフォルダ の順になる


### 選択対象に対して Plugin を動作させる

上にかいた `selection` を使います。現在選択されてるオブジェクトたちが入った NSArray

`[selection count]` で現在の選択数が取れる


```
// count を使ってみる
if ([selection count] == 0) {
    // nothing is selected 
}

// 回してみる
for (var i = 0; i < [selection count]; i++) {
    // do something with item
    var item = selection[i] 
}

// Obj-C みたいに回すこともできる
var loop = [selection objectEnumerator]

while (item [loop nextObject]) {
    // dosomething with item 
}

```

### 選択解除

`[page deselectAllLayers]` をつかう

```
// 現在のページで選択しているアイテムを解除する
[[doc currentPage] deselectAllLayers]
```


### アイテムを選択する

コードから指定したアイテムを洗濯する場合は、 MSLayer がもつ `select:byExpandingSelection` を使う


```
// 全選択解除
[[doc currentPage] deselectAllLayers]

var allLayers = [[doc currentPage] layers]

for (var i = 0; i < [allLayers length]; i++) {
    var layer = allLayers[i]

    if ([layer name] == "Rectangle") {
        [layer select:false byExpandingSelection:true]
    }
}
```


### ユーザ入力を取得する

`[doc askForUserInput]` を使って、ユーザからの値を得ることができる。また、 `initialValue` でデフォルトの値を入れておくこともできる。

```
var name = [doc askForUserInput:"What is your name?" initialValue:""]
```

*Note* `initialValue` は必須の引数。"" (空文字) を指定すれば空にできる。

より複雑なユーザ入力を行う場合は NSAlert クラスを用いる。

```
function createSelect(msg, items, selectedItemIndex) {
    selectedItemIndex = selectedItemIndex || 0
    
    var accessory = [[NSComboBox alloc] initWithFrame:NSMakeRect(0, 0, 200, 25)

    [accessory addItemsWithObjectValues:items]
    [accessory selectItemAtIndex:selectedItemIndex]

    var alert = [[NSAlert alloc] init]

    [alert setMessageText:msg]
    [alert addButtonWithTitle:'OK']
    [alert addButtonWithTitle:'CANCEL']
    [alert setAccessoryView:accessory]

    var responseCode = [alert runModel]
    var sel = [accessory indexOfSelectedItem]

    return [responseCode, sel]
}

var options = ['None', 'One', 'Two', 'Three']
var choice = createSelect('Choose an option', options, 1)

log(choice)
```

### フィードバック

ユーザに対して何らかのフィードバックを返す場合、 `[doc showMessage]` もしくは `[NSApplication displayDialog]` を使う。前者は一行のメッセージが表示でき、後者はタイトルと内容付きのアラートダイアログが表示できる

```
// [doc showMessage]
[doc showMessage:"The operation is complete"]

// NSApplication
var app = [NSApplication sharedApplication]

[app displayDialog:"The operation is complete" withTitle:"We're done"]
```