プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

Eclipseプラグイン開発: リソースパス関連

Eclipse プラグイン開発 目次


 Javaでファイル、ディレクトリを操作する場合、java.io.Fileやjava.nio.file.Pathを用いて表現します。

 EclipseではIPathないし、IResourceを用いて表現するのですが、これもうほんと、使いにくい。
 といっても、Eclipseでファイルを扱おうと思ったら避けることは出来ませんので、理解して付き合っていくしかありません。

IPath

 IPathはJavaのAPIで言えば、java.nio.file.Pathとほぼ同等の機能です。ファイルやディレクトリなどの場所(パス)を表現します。

 Eclipseプラグイン開発において、IPathは主に以下のパスを表現します。

  • フルパス:ワークスペースをルートとする、ワークスペース空間における絶対パス表現
  • ロケーション(ローカルシステム)パス:OS固有の絶対パス表現
  • 相対パス:絶対パスではないパス表現


f:id:nodamushi:20170403014824p:plain:right

 フルパスというのは、ワークスペースをルートとする、Eclipseシステム系の絶対パスを示します。
 たとえば、右図の様なワークスペースのプロジェクト構造をしているとき、AプロジェクトのA.javaは「/A/src/A.java」と表現され、BプロジェクトのdlinkフォルダのTest.classは「/B/dlink/Test.class」と表現されます。
 ルートであるワークスペースのパスは「/」です。この辺はUnixと同じ表現ですね。@でも何でも良いから別な文字にしときゃ良かったのに。

 

 一方で、ロケーションパスというのはOSのファイルシステムにおける絶対パスを表します。
 実際にフルパス、ロケーションパスを見たければ、プログラムを書かなくても、プロパティビューから確認することが出来ます。
 右図のワークスペースは、「F:\workspace」に配置しているのですが、プロジェクトAとプロジェクトBのローカルパスを表示すると下図の様に「F:\workspace」とは全然関係ないところに配置されていることが分かります。
 従って「ワークスペースのディレクトリ(F:\workspace)」+「フルパス」としてもOS上のファイルパスを取得することが出来るとは限りません。
f:id:nodamushi:20170403015926p:plain


 なお、ロケーションパスには、生ロケーションパス(Raw Location Path)が存在しますが、これは使うことないと思います。Eclipseのフォルダリンクは、リンク先を絶対パスで指定するタイプのものと、変数で指定する仮想リンクがあるのですが、生ロケーションパスが表現するのは後者のほうです。例えば、Test.javaはこんな風に出てきます。「PARENT-1-PROJECT_LOC/java/Test.java」。普通にロケーションパスを取得すれば、変数の所は解決してくれるので、考える必要はありません。

 相対パスは、Bundle内のリソースパスだったり、特定のパスからの相対パスだったり等、使う場所や条件によって意味が変わる通常の相対パスです。

IResource

 IResourceはワークスペース空間におけるリソース(ワークスペース、プロジェクト、フォルダ、ファイル)を表現するインターフェースです。ワークスペース空間におけるリソースというのは、要するにフルパスで表現できるリソースを表します。
 JavaのAPIでいうと、パス以外の付加情報や操作機能を持っているという点でjava.io.Fileがよく似ています。

 java.io.Fileと違うのは、ルート(ワークスペース)、プロジェクト、フォルダ、ファイルと4種類のインターフェースに派生する点と、IFile(ファイルを表現するIResource)から内容を編集したときに、Eclipseのローカル履歴に変更情報を残すことが出来る点でしょう。

IResource 説明 取得方法
IWorkspaceRoot ワークスペースを表す IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IProject プロジェクトを表す IProject project = root.getProject("B");
IFolder フォルダーを表す container.getFolder(new Path("hoge"));
IFile ファイルを表す container.getFile(new Path("hoge.txt"));


 containerはIWorkspaceRootか、IProject、IFolderのことです。IContainerのメソッドの引数として渡すIPathは、containerからの相対パスです。

 getProject,getFolder,getFileによるプロジェクト、フォルダー、ファイルの取得は、存在しないリソースも表します。この辺はもうAPI設計のミスだと思うわー。newProject,createFolderとかにして、存在する場合はエラーにすべきだと思うわー。

 IProject、IFolder、IFileに関わらず、存在する場合のみIResourceを取得したい場合は、container.findMemberメソッドを使います。
 

IPathからjava.io.Fileやjava.nio.Pathへの変換

 Eclipse内部だけで完結するならIPath,IResourceで全て処理すれば良いのだが、大概そうもいかない。普通のライブラリって、ファイル表現にFileだったりPathを使うわけで、IPathやIResourceのままだと困るのだ。

 結論を言うと、IPathやIResourceから確実にFileやPathに変換する方法はない。
 基本は「IPath→IResource→FileやPath」という変換手順を辿らなければならないが、「IPath→IResource」の段階で絶対の手順というものはないし、「IResource→FileやPath」も必ず成功するとは限らない。

IPath→IResource

 まずは渡されたIPathがフルパスなのか、ロケーションパスなのか、相対パスなのか考えなくてはならない。相対パスの場合は、何に対して相対なのかで話が変わるから、ここでは考えない。
 
 フルパスなのか、ロケーションパスなのかについては、Windowsの場合は楽です。「~:\」ないし「\\」から始まっていたらロケーションパスです。そのままFileやPathに変換しましょう。しかし、これがUnix系の場合だったら…どうすんだろうね。


 で、フルパスからIResourceに変換する一番簡単な方法は以下の方法だと思う。

IResource resource = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path("パス"));

 なお、パスが示す先がFolderであっても、ファイルとしてアクセスするような処理をしない限り、実はgetFileで特に問題ない。

 findMemberだと、存在しないファイルのパスを解決できないので、getFileを使うと良い。

IResource→File,Path

 単純には以下のようにすれば良い。しかし、プロジェクトを閉じている場合や、リンクを解決できない場合などで、getLocationはnullが返ることがあるので、要注意

File file = resource.getLocation().toFile();
Path path = Paths.get(resource.getLocation().toOSString());

Eclipseプラグイン開発: 開発環境の準備

Eclipse プラグイン開発 目次




 え?Eclipseプラグイン開発するつもりなんですか?まだ引き返せますよ?上司にVisual Studio Codeを提案した方がいいと思うよ?

 ちゃんと私は止めましたからね?

開発に必要なツール

  • 根性と泣かない心
  • GoogleとGithub
  • Eclipse IDE for Eclipse Committers
  • EASE
  • Maven

根性と泣かない心

 分からなくても泣かない。案外数ヶ月後にもう一度やってみたら分かったりする。

GoogleとGithub

 Eclipse開発で困難を極めるのは、目的を達成するために「何をどうすれば良いのか分からない」ことが多々あると言うことです。結論が「~~Interfaceを実装すれば良い」というだけのことであっても、「~~」にたどり着くのが結構大変。いや、マジで。

 とにかく、コーディング能力よりも検索能力が試されます。従ってGoogle先生は必須です。

 そして、GithubにはEclipse本体含む、多くのプラグインが公開されています。他のプラグインがどういう実装をしているのかを調べることは、非常に有用であり、重要な手がかりになります。私の場合、Githubの検索である程度当たりを付け、実際のソースコードはEclipse上で表示する、というやり方に落ち着きました。

参考URL

 増えてくると別ページに分離するかも。

 Eclipse Documentation HelpのPlatform Plug-in Developer Guide項目とひしだま氏のEclipseプラグイン開発はブックマークしておきましょう。
 ただ、Eclipse Helpのページ、サイドバーとかJavaScriptで動いてて非常に使いにくいんで、JavaScriptをoffにして目的のページだけ表示するようにした方が良いと思います。あと、ひしだま様は神様。

※EclipsepediaのSnippetが動かなくても泣かないコト

参考書籍


Eclipse Plug-in Development: Beginner's Guide - Second Edition (English Edition)

Eclipse Plug-in Development: Beginner's Guide - Second Edition (English Edition)

 Eclipseプラグイン開発そのものよりも、どっちかっていうとMavenのtychoの使い方を知るのにとても有用だった
 

Eclipse IDE for Eclipse Committers

 Eclipse Packages | The Eclipse Foundation - home to a global community, the Eclipse IDE, Jakarta EE and over 350 open source projects...からEclipseプラグインを開発する用のEclipseを用意しましょう。むろん、すでにEclipseを使っていて、それにプラグイン開発用プラグインをインストールしても構いません。

 特に気にしない場合、最新バージョンを選べば良いですが、対象のEclipseバージョンを限定、ないし最低バージョンが指定されている場合は、そのEclipseを選びましょう。APIが変わったり増えたりします。

EASE

 Eclipse Advanced Scripting Environment | The Eclipse Foundation

 Eclipseのプラグインで、Eclipseを実行している環境上でJavaScriptなどのスクリプト言語を動かすことが出来るプラグインです。ブラウザで言う、Firebugみたいな感じで私は使っていますが、プラグイン開発に慣れた人なら、EASEを使って、プラグインにしなくても高度なことがスクリプト的に実行できるとても便利なツールです。具体的には後に載せるYoutubeの動画を見ると分かりやすいと思います。

 Eclipseのプラグインを開発しているときに、APIがどういう動作をするのか説明読んでもよく分からないってことが結構ありますが、その動作確認のために、何度もEclipseを立ち上げ直すのはかなり面倒くさいです。EASEを使うと、そういったAPIの動作をその場で確認できます。

 ただ、v0.3まではかなり動作が不安定っていうか、しょっちゅうエラー出てました。v0.4でわりと結構使えるようになりましたが、それでも何かけっこー固まる。SWTのインスタンスにDisplayのThread以外からアクセスしようとすることが多々あるのよね…

 まだ現状動作が不安定なので、開発しているEclipseとは別のEclipseか、開発中のプラグインをテストしているEclipseで動かすのが無難でしょう。
 

Maven

Maven – Welcome to Apache Maven
Apache Maven3 (3.2.5) インストール手順 (Windows) | WEB ARCH LABO

 最近はMavenも廃れてGradleかと思うのですが、残念ながらGradleでのやり方知りません(オイ

 プラグインをビルドしたり配布する場合、Eclipseの機能をそのまま使っても良いですが、Mavenを使うことをお勧めします。特に、32bit環境、64bit環境、OS等によって配布するプラグインを変えたい場合はMavenが必須になります。


 

Eclipseプラグイン開発: 目次

 はい。皆さん。Eclipse使ってますか?え?IntelliJ?デスヨネー

 そんな、もう寂れたEclipse。もうプラグイン作ろうなんて人はこの世にいないんじゃないのかと思うけど、何故か作る羽目になったので、そのドキュメントを残しておこうと思います。

これからEclipseプラグイン開発を始めようと思う人へ

 悪いことは言わない。引き返せ。

 IntelliJか、Visual Studio Codeを先に検討した方がいいでしょう。個人的にはVS Codeが良いと思います。

目次(と言う名のTODOリスト)

 思いつく限り挙げてみたけど、以下の目次が完成することはあるんですかね?ていうか、たぶんまだ増えるんだけど。
 完成したら本にして良いんじゃないんですかねぇ。出版社の方、このクソニートをどこか拾ってくれませんかー

 なお、順番通りに記事にするとは限らない。

  1. 開発環境の準備
  2. 基礎知識
    1. OSGi
    2. プラグインプロジェクトとフラグメントプロジェクト
    3. IAdaptable
    4. パス関連(IPath,IResource等)
    5. バンドルリソース関連
    6. 非同期実行
  3. テストのやり方
    1. 非UIプラグインのテスト
  4. SWT基礎知識
    1. Binding
    2. Layout
  5. 拡張ポイント
    1. 拡張ポイントの定義
    2. 拡張ポイントの実装
  6. ビュー(パート)作成
    1. ビューの作成
    2. ツールバーの追加
    3. アクションツールバーに追加
    4. ステータスラインに追加
    5. 色、フォント設定
  7. 設定画面拡張
  8. メニュー拡張
    1. メニューバーに新規項目追加
    2. メニューバーの項目に新規アイテム追加
    3. ポップアップメニューに新規項目追加
    4. 他のプラグインのアイテムを無効化
  9. エディタ作成
    1. パーティション
    2. Position
    3. シンタックスハイライト
    4. コンテンツアシスト
    5. TM4Eによるエディタ開発
  10. プロジェクトの定義
    1. IProjectとIProjectDescription
    2. Nature
    3. プロジェクトアイコン
    4. プロジェクト設定ファイル
  11. パースペクティブ
  12. デバッガ作成
    1. ILaunchConfigurationを定義
    2. LaunchとLaunchDelegate
    3. LaunchDelegateを起動する
      1. Launch Configuration Tabから起動
      2. Launch Shortcutから起動
    4. IDebugTargetの定義
  13. Eclipse CDTの拡張
    1. 基礎知識
      1. ICProject
      2. Configuration
      3. SourceEntry
    2. ビルド
      1. インターナルビルダー
      2. ツール
      3. ツールチェイン
      4. ビルドマクロ

スマホがパソコンに取って代わるべきではない理由

wpb.shueisha.co.jp

 上記の記事を読んだからという訳ではないですが、私は常々スマホはパソコンに取って代わるべきではないと思っています。

 取って代われ「ない」ではなくて、取って代わる「べきではない」です。技術的には近い将来、取って代わられるでしょう。(むろん、全てが、とは言いません。)

距離とダメージ

 さて、私が取って代わるべきでない、と考える理由はデバイスとの「物理的な距離」です。

 技術が発展するにつれて、計算機と人間の物理的な距離はドンドン近くなっています。最初はどでかい建物だった計算機がいつの間にか卓上に乗るようになり、気がつけば手に乗り、腕時計や眼鏡として体に密着するようになってきました。最終的に行き着く距離は0どころか、人間の体内に埋め込む同一化でしょう。

 同一化まで技術が進歩した場合どうなるのかは分かりませんが、現状の技術では、どうしても「眼」への負担が大きくなってきています。

 私なんかは

  • 遠視
  • 弱視
  • 斜視
  • 乱視
  • 不同視
  • 飛蚊症
  • ドライアイ

と、眼の異常のオンパレードなので、よく分かるんですが、スマホって1時間も弄ってると眼へのダメージがやばいです。あぁ、後、眼圧も昔から若干高いそうで。もう、吸血鬼みたく目からビーム出したり、邪気眼の1つぐらい生えててもおかしくないんじゃないんですかね。

 昔から、近いモノを見続けることは、目に良くないと言われています。特にスマホの距離は乱視とドライアイに大ダメージで、うっかりスマホゲーとかぽちぽちやってると、その後2,3時間はぶれまくって、まともに見えなくなります。PCは仕事で10時間ぐらいまでなら弄っててもそこまでじゃない(10時間を越えると視界がやばい)んですが、スマホなら大体30分ぐらいが視界維持の限界です。Surfaceとかベッドに寝っ転がりながらよく弄っているのですが、文字を大きくして使っていても、2時間も弄ると眼がやばいです。


スマホに取って代わられると長時間使用が強制になる

 PCやTV、ゲームは目に悪い。そんなの随分と昔から言われてきたことで、今更な話です。

 しかし、だからといって、「健康のため、我が社は社員にPCをさせません」という会社があるでしょうか?無いですよね。多くの人がドライアイ等に悩みながら、長時間のPC作業を強制されているはずです。

 今やPC市場は斜陽産業で、生産数が落ちていけばスマホとPCの値段が逆転することもいずれ起こるでしょう。経費の問題からPCからスマホに移行ということもあるかもしれません。そうなると、PCと同様に、誰もが長時間スマホを使用することが強制されるはずです。

 こうなってくると、今は特にスマホ操作で異常を感じていない健常な眼を持っている人であっても、おそらく私と同じ症状が出てくるんじゃないかと思います。もしかすると、近距離でも目に優しいデバイスってのが出てくるかも知れませんが、そんなにすぐに出てくるとは思えません。(網膜に直接像を結ぶとか?)


 これを回避する手段として、スマホの外部端子を充実させて、周辺デバイスを利用するということが考えられます。しかし、経費削減を求める昨今の企業で、スマホが一つあれば仕事できるのに、追加のモニタやらキーボードやらを社員に配ってくれるってことはまずないでしょう。私の眼の悪さを理由に巨大なモニタを頂いてはいますけど、未だにSSDにはしてくれないんだよなぁ。(27インチディスプレイを1mは離して、フォントサイズ13以上じゃないと読む気がしない。一応、小さい文字も、まだ若いので眼筋を酷使すれば見えることは見えるけど、普通の人だったら15cmぐらいの距離の文字を読むぐらいには力を入れないといけないのです。)

PCスキルを身につけたらいいんじゃないかなぁ

 スマホとPCのどっちが効率的か?これについては、流石に2017年現在の今はまだPCの方に軍配が上がると思います。上がるんじゃないかなぁ?いや、正直なところ私がスマホ使わないから、よく分からんケド。

 でも、そのうちスマホもPCも大して効率に差が無いか、逆転するという職種も増えてくるでしょう。とはいえども、紙と計算機に比べれば、スマホとPCにはそこまで大きな効率差はないんじゃないでしょうか。

 ならばスマホにPCが取って代わられようとしたとき、我々の眼を守ってくれるのは、スマホよりもPCの方が圧倒的に効率よく作業できるというユーザーのPCスキルです。スマホにした所為で効率が下がった、PCの方がスキル的に効率が良いとなると、経営者も一考せざるを得ないでしょう。

 といっても、昨今PCが使えない若者とか、第二のディジタルディバイド世代とか言われているように、スマホが主流で、PCが使えない人というのが増えているそうで、望みが薄いのも実情でしょう。

 このことは、レガシーデバイスは消える運命なので、特に問題があるとは思えません。PCが使えない奴は~とか言う老害は消えろと私は思います。(老害共は最低限アセンブラぐらい書けるんだよな?)
 スマホの方が学習コストが安く、PCとの効率がさして変わらないなら、スマホを使うというのは正しい選択でしょう。

 が、これが原因で現代人の眼がさらに過酷な環境に置かれることにならないことを、切に願うのみです。

近年神社に人が多いなぁと思うこの頃

あけましておめでとうございます。今年の抱負は再来年から本気出すです(・ω・)

元旦は皆さんはどのように過ごすんでしょうかね?

私は家族+犬とともに地元の神社に行ってあまりの参拝客の多さに何もせずに引き返してきました。別に、髪も仏も信じていませんし。死後の世界も知ったこっちゃなくて、信じているのは自らの脂肪のみです。あふん


さて、その地元の神社というのは、別に有名な神社とかじゃなくって、ほんとに地元に密着したというか、もう、本当に普通の神社。の、はずなんですが、なんと道にあふれるほどの行列。年々なんか人が増えている気がします。

単に私の地元の人口が増えてきているのか、それとも、SNS等の効果なのか、不景気で神頼みなのか、不毛で髪頼みなのか。


靖国神社の参拝客が年々増えているのと何らかの関係性はあるのでしょうかね?観測しているのが一つの神社しかないし、ちょっとググってみても資料が見当たらないので、ほかの神社や地域でどうなっているのか少し興味があります。




という感じで、今年からは、割と雑記的な内容や不確実だったり、すごく短い技術記事を増やしていこうかなと思います。


これまでこのブログは、それなりに調べてまとめた技術記事ばかりで、私事は書かない方針でやってきました。でも、大学生のころと違ってそんなに時間かけて書くがしんどくなって、更新回数がめちゃめちゃ減ってたんですよね。一つの記事が5000文字とか、1万文字とか多すぎかなと。私の妹の卒論が5万文字だったそうで、それでも多いほうだったそうですよ。なるべく、1000文字以内で収めたいですね。

あ、でも私クソニートなので時間だけは糞あるんですけどね。体重の問題ですね。間違えました、体力の問題ですね。

あとはツイッターがいつ潰れるのかわからん、ってのもありますね。ツイッターで済ましてたようなことも全部ブログのほうに持ってこようかなと。


後は今年の夏ぐらいにブログの内容をまとめたHPを作りたいなぁなんて考えています。これは体力いるからやるかどうかわからないけど。今のところあまりやる気なし。



というわけで、今年もよろしくお願いします。

なお、空白や改行、HTML含めてちょうどこの記事が1000文字です。だいぶ楽。

JavaFX9からPlatformに追加されるAPIについて

 この記事はJavaFX Advent Calender 2016の17日目の記事になります。
 前日はid:skrbさんのSooner or Later - JavaFX in the Boxでした。
 明日はid:aoe-tkさんです。


 前回今年最後とか言ったな。あれは嘘だ。

 はい、というわけで、なんとJavaFX Advent Calender 2016に二度も顔出しですよ。この引きこもりの王者クソニート様が。誰かお仕事くらさい

 というのもですね、JavaFX9で追加される新機能は前回紹介した内容以外にも、PulseListenerの追加やPlatform.startupやら、他にも小さな機能の追加があるのです。これらの使い道が私には思いつかなかったか、そもそもあまり興味を持っていなかったかなどの理由でスルーしたのですが、実はスルーしてた中でPlatformにNested Event Loop関連のAPIが追加になっていたようなのです!

 いや、もうマジでこれ知らなかった。知ってたら前回の記事に絶対追加してた。というわけで、興奮してついうっかり2回も顔出しすることにしました。ほんとスマンかった。

 なお、このことはJavaFX: New & Noteworthy(※PDF)にも書いてあります。しかし、Nested Event Loopの話がPulseとPulse Listenerのページの間に挟まれているんですよね。Pulseとかあまり興味ないせいか、読むのをすっ飛ばした様で気がつかなかったんですね~。アホですね~。

 というわけで、Platformに追加されるAPI(主にNested Event Loop)について解説します。



Platform.startup

 Application.launch使う限り使わないと思われるAPI。SWTとかSwingから使うときとかを想定している模様。

 JavaFX Application Threadを生成直後に引数に渡したRunnableを実行するとかそんな感じっぽいけど、普通に使う場面はないと思う。ないよね?思いつかなかったから前回もスルーしたんだけど。





Platform.requestNextPulse

 Scene.addPostLayoutPulseListener,Scene.addPreLayoutPulseListenerと共に追加されたPulse関連のAPI。次のPulseの要求が出来る。

 JavaFXには我々が扱うJavaFX Application Threadの他にPrism Render Threadという、画面描画用のスレッドがあるのですが、この描画スレッドとJavaFX Application Threadを同期するイベントがPulseになります。

 JavaFXってマウスイベントとかにNodeを追随させようとしても、妙にマウスの動きからNodeの動きがずれるのが、改善されたりしないかとか思ったけど、全くそんなことはなかったぜ。

 これらPulse関連APIの使い道は、今のところ私には思いつきません。PulseListenerの方はレイアウトにどれぐらい時間かかってるか、デバッグ的な使い方くらいかなぁ。私の頭じゃ。



 Pulseについての詳細は以下のページを参照してください。
docs.oracle.com


Nested Event Loop

 さて、今回の記事の本題です。といっても、実はこのNested Event LoopはJavaFX初期からあって、3年前の私の記事でも実はついでに紹介しています。
nodamushi.hatenablog.com


 大ざっぱに書くと、JavaFX Application Threadは通常以下のような処理をしています。

 Nested Event LoopのAPIを使うと、この流れを以下の図のように、複製することが可能になります。

 複製と言っても、新しいスレッドが走っているとかではなくて、単純に呼び出した関数の中でイベント処理のループが走り始めると言うだけです。


 この機能がおもにどこで使われているかというと、showAndWaitメソッドです。showAndWaitでウィンドウが閉じるまで処理を待機しても、JavaFX Application Threadは停止することなく、他の処理を続けることが出来ます。この時に使われているのがNested Event Loopです。


 今までは、Stage.showAndWaitからしかアクセスできなかったこのNested Event LoopのAPIが公開され、いつでもどこでも使えるようになります。
 一番簡単に思いつく使い道で、かつ、効果が高いのは、Stageのダイアログと同様にNode単位でのモーダルダイアログでしょう。

f:id:nodamushi:20161211183827p:plain


 今までは、このようなことを実現しようとしたら、次のような2つに分かれた手順が必要でした。

  1. なんらかのイベントハンドラ等からダイアログ用のNodeを作り、対象ノードの操作を不可能にする。(ここで最初のイベントハンドラの処理終了)
  2. ダイアログのYesやCancelといったボタンが押されたときに動作するイベントハンドラが、残りの処理を行い、ダイアログを消去する。

f:id:nodamushi:20161211183828p:plain

 しかし、Nested Event Loopを使うと、一つのイベントハンドラの中で処理が完結します。

f:id:nodamushi:20161211183829p:plain


 これはかなりプログラムを書くのが楽ですね。
 最近はJavaもラムダを導入したり、実質finalが導入されたりと色々工夫されて、イベントハンドラを書くのが楽になってきましたが、それでも処理が二分割されるようなプログラムを書くのって面倒くさいんですよね。
 処理が二分割で済めば良いですが、何回かダイアログを表示して、しかもそのダイアログ内容が途中で分岐するような処理だったら、面倒なことこの上ないですよね。

 一つのEvent Handlerで処理が済むというのは、それだけで実装面からも保守管理の点からもメリットです。




Nested Event Loopの使い方

Nested Event Loopを利用するには、以下のフィールド変数(ローカル変数は駄目)が必要です。

private Object waitKey = null;
  • waitKey: Nested Event Loopを解除するのに必要になるキー。なくしたらexitできないので要注意。final宣言してしまっても良い。

 フィールド変数を用意したら、後は以下の関数を定義しておけば良いでしょう。
 なお、基本的にはJavaFX Application Threadからしか操作しないので、waitKeyをvolatile等にする必要はありません。

private Object waitKey = null;

public boolean isWait(){return waitKey!=null;}

// 戻り値はintなどに決まっているなら、それに変換しても良い。
protected Object enterWait(){
  if(!isWait()){
    waitKey = new Object();
    Object result = Platform.enterNestedEventLoop(waitKey);
    return result;
  }
  return null;
}
// 引数のresultは上↑のresultになる。
protected void exitWait(Object result){
  if(isWait()){
    Object key=waitKey;
    waitKey=null;
    Platform.exitNestedEventLoop(key,result);
  }
}

 そしたら、後はこんな感じ。

//ダイアログのNodeを作成完了後
cancelButton.setOnAction(e->exitWait(0));
okButton.setOnAction(e->exitWait(1));
noButton.setOnAction(e->exitWait(2));

//何かにダイアログを追加
targetPane.getChildren().add( dialog );

//ダイアログのボタンが押されるまで待機
boolean isOK = enterWait() == 1;

//ダイアログを削除
targetPane.getChildren().remove( dialog );

if(isOK){//OKなら処理
…
}

 むろん、Dialogクラスのように、ダイアログ表示機能と待機機能を何かのクラスにまとめておいても良いでしょう。



 このように、Nested Event Loopを使えば、処理の途中でGUIを介した処理の結果が必要である場面の記述が簡単になります。色々夢が広がリングですね。


 というわけで、今度こそ、良いお年を。

JavaFX9が良い感じになってきた件

 この記事はJavaFX Advent Calender 2016の9日目の@arachan@githubさんの記事になるそうです。
 前日は@skht777さんのJavaFXで動くプロ生ちゃんデスクトップマスコットを作る - Qiitaでした。
 明日は@Yucchi_jpさんのHitInfoを少しだけ…です。


 はい、テンプレってこんなもんで良いんですかね。
 何か今年やたらと参加者が少なくて盛り上がってる風がないですよね。寂しいですな。
 普段はこういうのは眺めてるだけで、関連記事とか書いてても参加しない奴なんですが、人が少なくて寂しいので、珍しく参加してみようかと。




今、JavaFX9が静かに熱い

 
 世にJavaFXが出てからというもの、派手に目立ったことはないように思います。

 私はJavaFX2からさわり初めて、本格的にJavaFX8から利用をしていますが、これまでの印象はおおよそこんな感じ。

  • JavaFX Script : そんな子はいなかった
  • JavaFX2 : え?WebView?あぁ、Qtですか?
  • JavaFX8 : そんな子がいるような気がする


 なんというか、なんだかんだちょっと凝ったことをしようとするとすぐに制限に引っかかったり、実装を追ってハックするような真似をしないといけなくて、正直に言えば、Swingの方が使いやすいと思っていました。

 しかし、そんなこれまでのバージョン達であっても、登場前や直後は話題にする人は結構話題に挙げていたのですが、JavaFX9の話題は全く盛り上がっていないようです。「JavaFX9」でググってみても、2016年12月現在、id:aoe-tkさんの記事ぐらいしかトップに出てきません。なお、JavaFX8ではいろんな記事が出てきます。

 たしかに、新しいコントローラなどが一切追加されない、非常に地味なアップデートしかありません。派手な話題は描きにくいかもしれません。


 だが、私は言いたい。


 JavaFX9はこれまでJavaFXにあった不満を多く解決してきたと。

 これまであってもなくてもぶっちゃけ変わらない、実に微妙な立ち位置でしかなかったあのJavaFXが。
 あのJavaFXがついにJavaFX9で、僅かに痒いところに手が届かないところまで来たと!



 そんなJavaFX9の良いところをちょっとだけ、紹介いたします。


Skinが公開された!

 皆さんもご存じ、ControlのSkinがついに、ついに、ついにjavafx.scene.control.skinパッケージとして公開されます。
 ほんと、ついにですよ。なんで最初から公開していないんですかね。


 本当にジミーな内容ですね。これが目玉の変更点なんだから、まー盛り上がりませんわな。(´・д・`)


 でも、今まではちょっと見た目を追加したいとか、ちょっと機能を追加したい等々、「ちょっとレールから外れたい」ということをやろうと思えば、com.sunパッケージをいやーな気分で操作するか、多大なコストを払って実装するか(それでも結局com.sumパッケージに行き着くんだけど)しかなかったんです。
 いや、もう、ホント、これで大手を振って色々実装できますよ。


 でも、今回公開されるのはSkinだけで、Behaviorは公開されません。BehaviorはControlの機能、動作を決定する重要なクラスです。com.sunパッケージ時代のSkinだと、コンストラクタの引数にBehaviorが要求されていたんですが、これらがなくなってしまいました。


 じゃぁ、Behaviorそのものがパージされてしまったのかというと、そうでもなくて、結局com.sunパッケージとして残っています。で、コンストラクタの引数ではなくて、コンストラクタの中で生成するようになってしまいました。うわぁ~ぃ(泣
 一応、privateなメソッドでgetBehaviorとかがあるので、そのうち公開する気なのかも知れませんね。気長に待っています。



 あ、そういえば、(少なくとも私が読んだ)JavaFX8までのBehaviorの動作って、次のようになっていたと思うんです。

  1. マウスイベントなどを受け取る
  2. マウスイベントなどからアクションに対応する「String」に変換する
  3. BehaviorにStringを投げる
  4. Stringに対応する処理を実行する


 全部が全部って訳ではないと思いますが、少なくともTextAreaのBehaviorは次のような感じに変わったようです。

  1. Behaviorがマウスイベントなどを受け取る
  2. イベントをキーとして、登録しているラムダを取得する
  3. ラムダを実行する


 見えないところも整理が進んでいるようですね。



Tooltipが良くなった!

 去年、私はこんな記事を書きました。
nodamushi.hatenablog.com

 要約すると、ざっと以下の二つが不満点。

  1. 表示の遅延、表示時間が変更できなくて不満だ
  2. フォーカス持っていないウィンドウでもツールチップが表示される上に、画面の最上にウィンドウが表示されてうざい。


 しかし、JavaFX9からは次の3つのPropertyが追加されます。

  • showDelay : 表示するまでの遅延時間。デフォルトは1秒
  • showDuration : 表示時間。デフォルトは5秒
  • hideDelay : マウスがNodeから離れてからTooltipが消えるまでの時間差。デフォルトは0.2秒
Label label = new Label("マウスを置いているとポップアップするよ。");
Tooltip tooltip = new Tooltip("すぐに現れて消えないTooltip!");
tooltip.setShowDuration(Duration.INDEFINITE);
tooltip.setShowDelay(Duration.ZERO);
label.setTooltip(tooltip);
Scene s = new Scene(label);
stage.setScene(s);
stage.show();

f:id:nodamushi:20161208203008p:plain

 いや~、ツールチップが消えないからキャプチャするのも急がなくて良いし楽だね。


 でも、残念なことに二つ目の不満点である、フォーカスを持たないウィンドウでもポップアップする挙げ句、ウィンドウを最上位まで持ち上げてきてしまう問題点は解決されていませんでした。



 これ結構な問題だと思うんだけど、中の人達に認識されていないのかな?



 あ、あと、これの解決方法が新しく追加されていないかと色々見てたときに気がつきましたが、JavaFX9からは現在表示しているJavaFXウィンドウの一覧をWindow.getWindows()で取得できるようになりました。これ、地味に嬉しい。
 私、これを自前で実装してたからね。

ObservableList<Window> windows = Window.getWindows()



子要素の順番を入れ替えなくても表示順序を変えられる!

 いままで、重なっている子要素の表示上下関係を入れ替えようと思ったら、getChildren()で子要素のリストを取得して、並びを入れ替えるしかありませんでした。
 Swingだったらzindexで変化するのに!と思っていたら、ついにJavaFX9でViewOrderプロパティが出来ました!


 ViewOrderは小さいほど手前に来ます。0が基準値です。負数も可能の様です。
 同じViewOrderの場合は子要素のリストに挿入された順番が反映される。

  Circle c1 = new Circle(100, 100, 100, Color.AQUA);
  Circle c2 = new Circle(100, 150, 100, Color.RED);
  Circle c3 = new Circle(300, 100, 100, Color.AQUA);
  Circle c4 = new Circle(300, 150, 100, Color.RED);
  pane.getChildren().addAll(
          c1,c2,//アクア色の円は赤より下
          c3,c4 //アクア色の円は赤より下
  );
  //c3,c4の表示順序を入れ替え、アクア色の円を赤の上に持ってくる
  c3.setViewOrder(0);//※0は初期値です
  c4.setViewOrder(1);

 実行結果の下図をご覧下さい。右側の二つの円はアクア色の円が赤色の円より上に表示され、左側の二つとは重なり方が変わっています。






念願の行番号付きテキストエディタが使える!

 JavaFX9で行番号付きのピュア(com.sunパッケージやリフレクションを利用しないという意味)なエディタが使えるようになります。
 主に私が作っていたアプリケーションの関係でどうしても欲しかったけど諦めてListViewとTextEditorを行き来することでなんとなくそれっぽい動作で諦めた、あの行番号付きエディタが!当時の私が知ったら号泣して喜んだでしょう。




 まぁ、これ、私が実装したんだけどね。ソースコードはGistにおいておきました。
JavaFX9でTextAreaに行番号がついたTextAreaを作ってみた · GitHub


 さて、先にも書いたように、ついにJavaFX9で今まで秘匿されてきたSkinの実装が公開されました。まだまだ公開して欲しいAPIはいっぱいあるのですが、重要なAPIは結構出そろってきました。その中の一つがText関連のAPIです。
 座標とTextの文字の位置を紐付けるHitInfoクラスTextInputControlSkinのgetCharacterBoundsメソッドが公開されたことにより、文字のインデックスとTextシェイプの座標内の対応が取れるようになりました。

 今回はこの二つを利用して行番号を表示すべき位置を取得し、TextAreaに行番号を付けてみました。


手順

 いい加減記事が長いので、詳細はソースコードを適当に眺めていただくとして、実装したものはザックリと説明すると以下の3手順です。

  1. まずは、TextAreaの文字列を行単位に分割する
  2. TextAreaの左上の座標と左下の座標の挿入位置を検出し、その範囲に入る行について3を処理する
  3. 各行の行頭文字のBoundsを取得し、その位置に会うように行番号を配置(空行などは上手いこと何とか処理する)


 まず、1の文字列を行単位に分割するのは別に説明は要らないでしょう。
 3の、表示位置の取得は、各行の行頭文字インデックスが分かれば、getCharacterBounds(index)で簡単に取得可能です。で、問題は2でした。


HitInfoの落とし穴

 さて、TextInputControlSkinから挿入位置を取得するには、getIndex(x,y)でHitInfoを取得するか、getInsertionPoint(x,y)で取得します。これらはどっちも結果は同じです。
 さて、画面左上の座標の挿入位置と、左下の座標の挿入位置を検出したいのだから、単純に考えるとこうなるはず。

int topInsert = getInsertionPoint(0,0);
int bottomInsert = getInsertionPoint(0,height);


 私は最初これで実装して、行番号が表示されなくて悩みました。

 これで取得されるtopInsertとbottomInsertは、topInsertは0で固定、bottomInsertは-1もしくは、画面の大きさ依存の値で飽和するかになります。

 と、言うのもここでの(0,0)や(0,height)の座標は、TextAreaの座標ではなく、あくまでText内での座標を指しています。従って、Textの原点位置での挿入場所は当然最初の0になってしまうし、(0,height)と指定しても、スクロールバーとか考慮されていないので、ある値で飽和してしまう。-1が返ってくるのは、(0,height)の範囲にTextシェイプが存在していなかったからかも知れないが、これに関しては詳しくはまだ追っていません。

 従って、目的のことがしたい場合は次のようにオフセットを考慮してあげる必要があります。

double offsetY = getCharacterBounds(0).getMinY();
int topInsert = getInsertionPoint(0,-offsetY);
int bottomInsert = getInsertionPoint(0,height-offsetY);
if(bottomInsert < 0)bottomInsert = textArea.getLength();

 最初の文字のY座標をオフセットとして引いてやると、上手くいく。
 bottomInsertは-1になる可能性があるから、-1になっていたら、ひとまずTextAreaの文字列長に設定してやれば良いと思われる。



 まぁ、これはHitInfoの落とし穴というよりは、getIndex(x,y)の仕様ミスだと思うけど。
 実際に公開するまでに変わったりするかもね?変えた方が良いと思うよ?


みんなJavaFX9使おうぜ!

 はい、というわけで、どうでしたでしょうか?
 これまでは、「はぁ~、これもできねぇのかよ」とか、「あ~、動きがもっさり」とか、「JavaでもQtでよくね?Swingでよくね?」「男なら黙ってCUI」などなど、散々(主に私に)捻られてきたJavaFXですが、かなり良い子になってきたと思います。

 Swingとは使い勝手がだいぶ違うので、今までSwingをやってたって人だと、それなりに学習コストを払わなくてはなりません。しかし、それを払うだけの価値はJavaFX9でそれなりに出てきたと思います。


 まだ画面がなーんかちらつくとか、3DShapeとかいらねーからOpenGL使わせろとか、シェーダー書かせろとか、KyeEventが使いにくいとか、Behaivor公開しろとか、言いたいことは山ほどありますが。まぁ、要求なんて何年たっても0になることはないので、焦ってもしょうがないですね。



 ぜひ、この機会にJavaFXを本格的に使いはじめてみませんか?ということで、今回の記事はおしまいです。

 最後まで読まれた方がいましたら、長々とおつきあいただきありがとうございました。





 実は、この記事は5日前に最後の行番号付きTextAreaの話だけで公開しようとしてたんです。でも、ついでにAdventカレンダーに登録してやろっかなーと思ったところ、明日のYucchi_jpさんと内容被りそうだったので、誤魔化すために色々追加したらこんなに長くなりましたとさ。も~、今年のブログ更新はこれで最後でいいな。良いお年を。