Electron に Mac タッチバー API が実装された
個人的に気になっていた Electron のタッチバーサポートがついに master にマージされました.
実装は下記の PR で行われ,@MarshallOfSound さんの初期実装と @kevinsawicki さんのブラッシュアップで実装されました.
https://github.com/electron/electron/issues/8095
まだリリースされていないですが,待てなかったので master ブランチの実装を試してみました.
追記(2017/3/8): v1.6.3 beta としてリリースされました.
Electron の master ブランチをビルドする
ビルドの仕方はドキュメントにまとめられています. 再配布せず手元で試すだけであれば面倒そうな「macOS SDK」のセクションは無視してしまって大丈夫です.
# リポジトリを取得 $ git clone https://github.com/electron/electron && cd electron/ # ビルドに必要な依存関係パッケージの取得 $ ./script/bootstrap.py -v # ビルド $ ./script/build.py
ビルドにはそれなりに時間がかかります.out/R
にリリースビルドの Electron.app
が,out/D
にデバッグビルドの Electron.app
が生成されます.
Touchbar API
タッチバーはメインプロセスから制御することができます.レンダラプロセスからは IPC 経由でのアクセスになりそうです. macOS 10.12.1 以降のタッチバー搭載 MacBook Pro で利用できるようです
タッチバーの UI パーツにはいくつかの種類があり,各パーツごとの class と,それらをまとめる class が提供されています.
TouchBarButton
: アイコンとテキストが置けるボタンですTouchBarColorPicker
: カラーピッカーですTouchBarGroup
: 複数の UI パーツを1つのグループとして配置できますTouchBarLabel
: テキストを表示できるラベルですTouchBarPopover
: アイコンとラベルとキャンセルボタンが表示できる,タッチバーに出るダイアログボックス的な UI ですTouchBarSlider
: 音量などに使えるスライダーですTouchBarSpacer
: タッチバーのアイテムとアイテムの間にスペースを開けることができます.開けるスペースはsmall
,large
,flexible
(スペースを取れるだけ取る) の中から選べますTouchBar
:TouchBar
は上記 Touchbar の UI パーツを並べてタッチバーインスタンスを生成できます
タッチバーインスタンスは BrowserWindow
の setTouchbar()
メソッドによってセットできます.セットされると即座にタッチバーが表示されます.また,setTouchbar
メソッドに null
を与えることでタッチバーを消すことができます.
サンプルアプリ
ドキュメントにのっているサンプルアプリを動かしてみます.ドキュメントにはコード片のみなので,動くコードを下記に置いてみました.
サンプルアプリではタッチバー上でスロットを回すことができます.
エントリポイントである main.js
は下記の通りです.
const path = require('path') const {app, BrowserWindow, TouchBar} = require('electron') // 必要なタッチバーの UI パーツをインポートする const {TouchBarLabel, TouchBarButton, TouchBarSpacer} = TouchBar let spinning = false // スロットの各リールの文字列を表示するラベル3つ const reel1 = new TouchBarLabel() const reel2 = new TouchBarLabel() const reel3 = new TouchBarLabel() // スロットを回した結果を表示するラベル const result = new TouchBarLabel() // 'Spin' ボタン const spin = new TouchBarButton({ label: '🎰 Spin', // ボタンのテキスト.ここでは絵文字だけどアイコンも置けるはず… backgroundColor: '#7851A9', click: () => { // スロットが回っている間はクリックを無視する if (spinning) { return } // スロットを回す処理 spinning = true result.label = '' let timeout = 10 const spinLength = 4 * 1000 // 4 seconds const startTime = Date.now() // クリックされてから4秒間スロットを回す const spinReels = () => { // スロットのリールを更新する.止まった時のリールが結果になる updateReels() if ((Date.now() - startTime) >= spinLength) { finishSpin() } else { // 1.1倍ずつスロットを遅くしていく(慣性っぽさ) timeout *= 1.1 setTimeout(spinReels, timeout) } } spinReels() } }) // ランダムに絵柄を1つ選ぶ関数 const getRandomValue = () => { const values = ['🍒', '💎', '7️⃣', '🍊', '🔔', '⭐', '🍇', '🍀'] return values[Math.floor(Math.random() * values.length)] } // 3つのリールを更新 const updateReels = () => { reel1.label = getRandomValue() reel2.label = getRandomValue() reel3.label = getRandomValue() } const finishSpin = () => { // Set を使って重複を省き,残った要素数で絵柄がいくつ合ったかをチェックする const uniqueValues = new Set([reel1.label, reel2.label, reel3.label]).size if (uniqueValues === 1) { // すべての絵柄が揃ったとき:大当たり! result.label = '💰 Jackpot!' result.textColor = '#FDFF00' } else if (uniqueValues === 2) { // 3つ中2つのリールが同じ絵柄だったとき:当たり result.label = '😍 Winner!' result.textColor = '#FDFF00' } else { // 絵柄がまったく揃わなかったとき:はずれ result.label = '🙁 Spin Again' result.textColor = null } spinning = false } // [ボタン] [r1] [r2] [r3] [result] // // のように配置.配列に入れたパーツが入れた順にタッチバーに表示される const touchBar = new TouchBar([ spin, new TouchBarSpacer({size: 'large'}), reel1, new TouchBarSpacer({size: 'small'}), reel2, new TouchBarSpacer({size: 'small'}), reel3, new TouchBarSpacer({size: 'large'}), result ]) let window app.once('ready', () => { window = new BrowserWindow({ width: 200, height: 200 }) window.loadURL(`file://${path.join(__dirname, '/index.html')}`) // ブラウザウィンドウにタッチバーをセットする. // このタイミングでタッチバーにスロットマシンが表示される window.setTouchBar(touchBar) }) // 全てのウィンドウが閉じたときアプリを閉じる app.on('window-all-closed', () => { app.quit() })
まとめ
ついに Electron アプリでもタッチバーにアクセスできるようになりました. まだ入ったところなので多少バグなどがあるかもしれませんが,面白い機能なので是非自分がつくっているアプリでも活用を考えたいと思います. 何事も無ければ次リリースに含まれると思うので,今週後半か来週あたりには v1.6 系で使えるようになるのではと思います.
GitHub のハイライトがおかしい時のなおしかた(GitHub のハイライトの仕組み)
GitHub でコードを見ていると,ハイライトがおかしいのを見つけることがたまにあります.ここではその直し方を紹介します.
次の2パターンを想定しています.
- 自分のリポジトリのあるファイルのハイライト言語がおかしい
- 構文ハイライトが間違えている,言語の新機能に対応していない
自分のリポジトリのあるファイルのハイライト言語がおかしい
コードをどの言語でハイライトするかは自動判別されますが,実はある程度ユーザ側で制御する方法があります.
Vim や Emacs のモードラインを書く
ファイル単位で Vim や Emacs の設定をマジックコメントとして書けるモードラインですが,実は GitHub もそれを参照しています
// C++ だけど拡張子は .h … C じゃなくて C++ でハイライトしてほしい // vim: set ft=cpp // // または // // -*- mode: cpp;-*-
これで上記のコードは C++ として認識されるはずです.
.gitattributes
に指定する
リポジトリの .gitattributes
ファイルにハイライトを指定することもできます.
$ cat .gitattributes *.h linguist-language=C++
またハイライト以外の設定もできて,
$ cat .gitattributes vendor/* linguist-vendored docs/*.rb linguist-documentation=false
例えば上記の設定をすると,vendor/
配下は他のプロジェクトのコードと判断してリポジトリの stat (リポジトリページのヘッダにあるコード率など)から除外できたり,docs/*.rb
はドキュメント向けのコードとして,stat カウントから除外できたりします.
構文ハイライトが間違えている,言語の新機能に対応していない
実は GitHub のコードハイライトを行うライブラリは OSS で公開されています.
https://github.com/github/linguist
このリポジトリでは
など様々なメタデータが管理されています.なので例えば新しい言語をつくって十分に普及してきたら,ここに自分の言語を追加したプルリクを出すなどもできるわけですね.
今回は一番最後の言語のハイライト方法を見ます.
GitHub のハイライトは .tmbundle
という,TextMate の構文ハイライトフォーマットまたは,Atom の cson を使った構文ハイライトフォーマットを利用してハイライトされています.これらはオープンソースで開発されているものを利用しており,一覧は下記のディレクトリにあります.
https://github.com/github/linguist/tree/master/vendor/grammars
サブモジュールでリビジョン固定されていますが,何らかのタイミングで更新されているので,対象の .tmbundle
や .cson
を管理しているリポジトリにプルリクを投げて修正してもらえばいずれ linguist の依存する構文ハイライターもアップデートされ,GitHub 上のハイライトがなおるという仕組みです.
例えば僕は以前 Vim script の try ... finally
がハイライトされていないことに気づいたので,それを追加するプルリクを投げました.
また,上記リポジトリでは各種フレームワーク対応(例えば jquery.*.js
は jQuery のファイルなど)も行われているので,各フレームワーク向けのハイライトを改善したい時は YAML の設定ファイル にプルリクを出せば良さそうです.
GitHub でコードを読んでいるとハイライトが壊れていて困ることがあるかもしれませんが,実は自力で修正できますというご紹介でした.
公式ドキュメント
https://github.com/github/linguist/blob/master/docs/overrides.md
Travis CI で Linux (x86_64, i686, aarch64) 向け(とついでに macOS 向け)に Rust で書いたツールのバイナリをリリースする
git-brws のリリース で微妙に Travis CI 上の rust 環境でハマったのでメモ.
前提として,cargo build
でビルドできるものとします.Travis の環境はツールチェーンが古いと困るので Ubuntu 14.04 を使ってます.今回は下記の環境向けのバイナリをビルドしてリリースしました.
Linux は musl libc を使う選択肢もありますが,使い慣れた glibc にしました.
3行で
ビルド環境
まずは matrix:
でビルドを回すジョブを書いておきます.これによって,1回の CI で4つのビルドが走ります.
matrix: include: - os: osx rust: stable env: TARGET=x86_64-apple-darwin - os: linux rust: stable env: TARGET=x86_64-unknown-linux-gnu - os: linux rust: stable env: TARGET=i686-unknown-linux-gnu - os: linux rust: stable env: TARGET=aarch64-unknown-linux-gnu
cargo
でビルドするときにターゲットを指定する必要があるので $TARGET
変数に triple を指定しておきます.未指定だとデフォルトのターゲットが使われるので実質必要なのはクロスコンパイルが必要な i686-unknown-linux-gnu だけですが,Travis のビルド一覧で分かりやすいので書いておきます.
os:
は osx
だと macOS が,linux
だと Ubuntu 14.04 が使われます.
Deploy 設定を書く
deploy: provider: releases api_key: secure: XXXXX file: git-brws-${TRAVIS_TAG}-${TARGET}.zip skip_cleanup: true on: repo: rhysd/git-brws # On specific condition -> condition: $TRAVIS_RUST_VERSION = nightly # # Only deploy tagged commit tags: true
初期設定は travis
コマンドがやってくれるので,gem install travis
して travis setup releases
するいつものやつで生成し,そのあと自分用の設定を足します.
今回はビルド後の before_deploy
で zip に固めているので,生成物を削除されないように skip_cleanup
を付けています.また,タグを打ったときだけリリースしてほしいので,tags: true
を指定してます.
ここまでが Travis の一般的なリリース設定です.
rustup を自前で入れる
Travis の rust ツールチェーンの管理は,rustup がまだシェルスクリプトで書かれていた頃のものを使っているようなので,最新の rustup
コマンドを入れます.
before_script: - sh ~/rust/lib/rustlib/uninstall.sh - export PATH="$PATH:$HOME/.cargo/bin" - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain=$TRAVIS_RUST_VERSION - rustc -V - cargo -V
$TRAVIS_RUST_VERSION
でインストールされるツールチェーンのバージョンを指定しているので,新しい rustup
でも同じバージョンの Rust がインストールされます.
ここは Travis の環境が更新されればいずれ要らなくなるかもしれません.
ターゲットの追加
i686 向けのビルドにはクロスコンパイルする必要があるので,i686 向けのツールチェーンを入れます.
before_script: - if [[ "${TARGET}" == "i686-unknown-linux-gnu" ]]; then rustup target add $TARGET; fi
また,aarch64 向けの gcc はコマンド名が aarch64-linux-gnu-gcc
なので,cargo
がリンク時にそっちを使うようにします..cargo/config
に TOML 形式でビルド設定を書けます.
before_script: - | if [[ "${TARGET}" == "aarch64-unknown-linux-gnu" ]]; then rustup target add $TARGET aarch64-linux-gnu-gcc -v mkdir -p .cargo echo "[target.${TARGET}]" > .cargo/config echo "linker = \"aarch64-linux-gnu-gcc\"" >> .cargo/config cat .cargo/config fi
また,今回は glibc を使ってビルドするので,リンクに 32bit 環境の gcc を入れます.これには apt addon を使って gcc-4.8-multilib パッケージをインストールしておけば OK です.gcc-multilib は gcc 4.4 でかなり古いので gcc-4.8-multilib にしました.
addons: apt: packages: - gcc-4.8-multilib - gcc-4.8-aarch64-linux-gnu - gcc-aarch64-linux-gnu - libc6-arm64-cross - libc6-dev-arm64-cross
ビルドして zip に固める
ここは普段通りリリースビルドして成果物を入れるディレクトリをつくり,zip に固めるだけです.各環境向けに $TARGET
を指定してやります.
before_deploy: - cargo build --target $TARGET --release - mkdir "git-brws-${TRAVIS_TAG}-${TARGET}" - cp target/$TARGET/release/git-brws LICENSE.txt README.md "git-brws-${TRAVIS_TAG}-${TARGET}" - zip "git-brws-${TRAVIS_TAG}-${TARGET}.zip" -r "git-brws-${TRAVIS_TAG}-${TARGET}"
これで tag 付きのコミットが push されるとクロスビルドに必要なツールが入り,ビルドされ,成果物が zip で GitHub のリリースページにアップロードされるはずです.
また,上記の設定で macOS でもビルド及びリリースできているはずです.
今手元に Windows マシンが無いのでできませんが,AppVeyor でのビルドも try してみる予定です.