演習中に「ダブルクリックとシングルクリックを区別する方法がわからない」という質問があったので作ったサンプルコードです.
ダブルクリックやトリプルクリックは複数のクリックの組み合わせで構成されているので,最初のクリックをシングルクリックと判定してしまうとダブルクリック操作が必ずシングルクリックの動作を誘発しておかしなことになります.
この問題に対応するには,最初のクリックのときにちょっと待って,後続のクリックがないことを確認する必要があります.要するに短時間だけイベント処理が眠ってくれればいいのですが,アプリケーション全体が眠ってしまうと,後続のクリックを受け取ることができなくなるのでもとの黙阿弥です.そこで,まずイベント処理を独立したスレッドとして実行します.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package wakita.lx14 | |
import scala.compat.Platform | |
import scalafx.Includes._ | |
import scalafx.application.JFXApp | |
import scalafx.application.JFXApp.PrimaryStage | |
import scalafx.scene.Scene | |
import scalafx.scene.input.{MouseEvent} | |
import scalafx.scene.layout.{Pane} | |
object Clicks extends JFXApp { | |
val canvas = new Pane { } | |
val DOUBLE_CLICK_WAIT_MS = 300; // milliseconds | |
var clickedAt = Long.MinValue | |
canvas.onMousePressed = { (ev: MouseEvent) => | |
clickedAt = Platform.currentTime | |
new Thread { | |
override def run { | |
val clicked = clickedAt | |
Thread.sleep(DOUBLE_CLICK_WAIT_MS) | |
if (clicked >= clickedAt) println(f"#Clicks = ${ev.clickCount}") | |
else println(f"Click(${ev.clickCount}) is ignored") | |
} | |
}.start | |
} | |
stage = new PrimaryStage { | |
title = "Click Test" | |
scene = new Scene(600, 400) { | |
root = canvas | |
} | |
} | |
} |
面倒くさそうな気もしましたが,やってみたら簡単でした.
FacebookでN村くんから指摘を受けたんですが,DOUBLE_CLICK_WAIT_MS を 300 より大きくするとバグが生じます.たとえば,この値を 2000 に設定すると(つまり,二つのクリックの間隔が2秒以内ならばダブルクリックとして扱う),すごくトロいダブルクリックが実現できそうなのですが,実際には最初のクリックは無視され,二番目のクリックはシングルクリックとして扱われます.
この原因を理解するには ScalaFX の元となっている JavaFX の仕組みを理解しなくてはいけません.JavaFX がクリックの間に 300ms 以上の間隔を検知すると自動的に clickCount を 0 に初期化するのです.いつか 0 にならないと困りますからね.
ということで,注意してお使い下さい.