diff --git a/doc/xtouchmini-mcu-midi-map.txt b/doc/xtouchmini-mcu-midi-map.txt
new file mode 100644
index 0000000..5e2863c
--- /dev/null
+++ b/doc/xtouchmini-mcu-midi-map.txt
@@ -0,0 +1,100 @@
+## input
+
+x90 x28 key 2 x00 off
+ x01 blink
+ x7f on
+x90 x29 key 3 x00 off
+ x01 blink
+ x7f on
+x90 x2a key 4 x00 off
+ x01 blink
+ x7f on
+x90 x2b key 5 x00 off
+ x01 blink
+ x7f on
+x90 x2c key 6 x00 off
+ x01 blink
+ x7f on
+x90 x2d key 7 x00 off
+ x01 blink
+ x7f on
+x90 x54 key 16 x00 off
+ x01 blink
+ x7f on
+x90 x55 key 17 x00 off
+ x01 blink
+ x7f on
+x90 x57 key 8 x00 off
+ x01 blink
+ x7f on
+x90 x58 key 9 x00 off
+ x01 blink
+ x7f on
+x90 x59 key 0 x00 off
+ x01 blink
+ x7f on
+x90 x5a key 1 x00 off
+ x01 blink
+ x7f on
+x90 x5b key 10 x00 off
+ x01 blink
+ x7f on
+x90 x5c key 11 x00 off
+ x01 blink
+ x7f on
+x90 x5d key 13 x00 off
+ x01 blink
+ x7f on
+x90 x5e key 14 x00 off
+ x01 blink
+ x7f on
+x90 x5f key 15 x00 off
+ x01 blink
+ x7f on
+
+xb0 x30--x37 vpot 0--7 x00 off
+ x01--x0b 0100000000000--0000000000010
+ x10 off
+ x11--x15 0111111000000--0000011000000
+ x17 0000001000000
+ x17--x1b 0000001100000--0000001111110
+ x20 off
+ x21--x2b 0100000000000--0111111111110
+ x30 off
+ x31--x36 0000001000000--0111111111110
+
+## output
+
+vpot 0--7 x90 x20--x27 x00 off
+ x7f on
+key 0 x90 x59 x00 off
+ x7f on
+key 1 x90 x5a x00 off
+ x7f on
+key 2--7 x90 x28--x2d x00 off
+ x7f on
+key 8 x90 x57 x00 off
+ x7f on
+key 9 x90 x58 x00 off
+ x7f on
+key 10 x90 x5b x00 off
+ x7f on
+key 11 x90 x5c x00 off
+ x7f on
+key 10 x90 x5b x00 off
+ x7f on
+key 12 x90 x56 x00 off
+ x7f on
+key 13-15 x90 x5d--x5f x00 off
+ x7f on
+key 16 x90 x54 x00 off
+ x7f on
+key 17 x90 x55 x00 off
+ x7f on
+
+vpot 0--7 xb0 x10--17 x01--x07 up
+ x41--x47 down
+
+fader xe8 x00 x40--x7f,x00--x3f (-64--63)
+
+
diff --git a/pom.xml b/pom.xml
index bd80c51..e2d3e17 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
org.janelia.saalfeldlab
saalfx
- 1.0.1-SNAPSHOT
+ 1.1.0-SNAPSHOT
Saal FX
Saalfeld lab JavaFX tools and extensions
diff --git a/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/Action.kt b/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/Action.kt
index 4cdba9e..b1abab0 100644
--- a/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/Action.kt
+++ b/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/Action.kt
@@ -74,7 +74,9 @@ import java.util.function.Consumer
open class Action(val eventType: EventType) {
val logger: Logger by lazy {
- LoggerFactory.getLogger(name ?: this.toString())
+ val simpleName = this::class.simpleName?.let { ".$it" } ?: ""
+ val name = ".${name ?: "event-${eventType.name}"}"
+ LoggerFactory.getLogger("saalfx.action$simpleName$name")
}
/**
@@ -178,7 +180,7 @@ open class Action(val eventType: EventType) {
when {
keysDown!!.isEmpty() && !keysExclusive -> true
keysDown!!.isEmpty() -> noKeysActive().also { if (!it) logger.trace("expected no keys, but some were down") }
- keysExclusive -> areOnlyTheseKeysDown(*keysDown!!.toTypedArray()).also { if (!it) logger.trace("expected only these keys: ${keysDown}, but but active keys were: (${getActiveKeyCodes(true)}") }
+ keysExclusive -> areOnlyTheseKeysDown(*keysDown!!.toTypedArray()).also { if (!it) logger.trace("expected only these keys: ${keysDown}, but active keys were: (${getActiveKeyCodes(true)}") }
else -> areKeysDown(*keysDown!!.toTypedArray()).also { if (!it) logger.trace("expected keys: $keysDown, but some were not down") }
}
} ?: let {
@@ -303,12 +305,13 @@ open class Action(val eventType: EventType) {
/* isValid(event) will only be true if event is E */
action(event)
} catch (e: Exception) {
+ logger.debug("Exception caught: ${e.message}")
exceptionHandler?.invoke(e) ?: throw e
}
if (consume) {
event?.consume()
}
- logger.trace("$name completed successfully")
+ logger.debug("completed successfully")
true
} else {
if (!isConsumed) {
diff --git a/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/ActionSet.kt b/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/ActionSet.kt
index 6faa435..b395792 100644
--- a/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/ActionSet.kt
+++ b/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/ActionSet.kt
@@ -108,10 +108,15 @@ open class ActionSet(val name: String, var keyTracker: () -> KeyTracker? = { nul
* @return the created and configured [Action]
*/
fun action(eventType: EventType, withAction: Action.() -> Unit = {}): Action {
- return Action(eventType)
- .also { it.keyTracker = this.keyTracker }
- .apply(withAction)
- .also { addAction(it) }
+ return Action(eventType).apply {
+ keyTracker = this@ActionSet.keyTracker
+
+ /* set the default name */
+ name = this@ActionSet.name
+
+ withAction()
+
+ }.also { addAction(it) }
}
/**
@@ -160,10 +165,12 @@ open class ActionSet(val name: String, var keyTracker: () -> KeyTracker? = { nul
*/
@JvmSynthetic
fun keyAction(eventType: EventType, withAction: KeyAction.() -> Unit = {}): KeyAction {
- return KeyAction(eventType)
- .also { it.keyTracker = this.keyTracker }
- .apply(withAction)
- .also { action -> addAction(action) }
+ return KeyAction(eventType).apply {
+ keyTracker = this@ActionSet.keyTracker
+ /* set the default name */
+ name = this@ActionSet.name
+ withAction()
+ }.also { addAction(it) }
}
/**
@@ -175,18 +182,19 @@ open class ActionSet(val name: String, var keyTracker: () -> KeyTracker? = { nul
*/
operator fun EventType.invoke(withKeys: KeyCodeCombination, withAction: KeyAction.() -> Unit): KeyAction {
/* create the Action*/
- val keyAction = KeyAction(this)
- .also { it.keyTracker = this@ActionSet.keyTracker }
+ return KeyAction(this).apply {
+ keyTracker = this@ActionSet.keyTracker
- /* configure based on the withKeys paramters*/
- keyAction.ignoreKeys()
- keyAction.verify { withKeys.match(it) }
+ /* set the default name */
+ name = this@ActionSet.name
- /* configure via the callback*/
- keyAction.apply(withAction)
- addAction(keyAction)
+ /* configure based on the withKeys paramters*/
+ ignoreKeys()
+ verify { withKeys.match(it) }
- return keyAction
+ /* configure via the callback*/
+ withAction()
+ }.also { addAction(it)}
}
/**
@@ -199,24 +207,23 @@ open class ActionSet(val name: String, var keyTracker: () -> KeyTracker? = { nul
operator fun EventType.invoke(vararg withKeys: KeyCode, withAction: KeyAction.() -> Unit): KeyAction {
/* create the Action*/
- val keyAction = KeyAction(this)
- .also { it.keyTracker = this@ActionSet.keyTracker }
+ return KeyAction(this).apply {
+ keyTracker = this@ActionSet.keyTracker
+
+ /* set the default name */
+ name = this@ActionSet.name
- /* configure based on the withKeys paramters*/
- keyAction.apply {
- if (this.eventType == KeyEvent.KEY_RELEASED) {
+ /* configure based on the withKeys paramters*/
+ if (eventType == KeyEvent.KEY_RELEASED) {
ignoreKeys()
keysReleased(*withKeys)
} else if (withKeys.isNotEmpty()) {
keysDown(*withKeys)
}
- }
-
- /* configure via the callback*/
- keyAction.apply(withAction)
- addAction(keyAction)
- return keyAction
+ /* configure via the callback*/
+ withAction()
+ }.also { addAction(it) }
}
/**
@@ -227,20 +234,20 @@ open class ActionSet(val name: String, var keyTracker: () -> KeyTracker? = { nul
* @param withAction [KeyAction] configuration callback
* @return the [KeyAction]
*/
- operator fun EventType.invoke(keyBindings: NamedKeyCombination.CombinationMap, keyName: String, withAction: KeyAction.() -> Unit): KeyAction {
+ operator fun EventType.invoke(keyBindings: NamedKeyCombination.CombinationMap, keyName: String, keysExclusive: Boolean = false, withAction: KeyAction.() -> Unit): KeyAction {
- /* create the Action*/
- val keyAction = KeyAction(this)
- .also { it.keyTracker = this@ActionSet.keyTracker }
- /* configure based on the keyBinding paramters*/
- keyAction.keyMatchesBinding(keyBindings, keyName)
+ /* create the Action*/
+ return KeyAction(this).apply {
+ keyTracker = this@ActionSet.keyTracker
+ name = "${this@ActionSet.name}.$keyName"
- /* configure via the callback*/
- keyAction.apply(withAction)
- addAction(keyAction)
+ /* configure based on the withKeys paramters*/
+ keyMatchesBinding(keyBindings, keyName, keysExclusive)
- return keyAction
+ /* configure via the callback*/
+ withAction()
+ }.also { addAction(it) }
}
/**
@@ -264,11 +271,13 @@ open class ActionSet(val name: String, var keyTracker: () -> KeyTracker? = { nul
): MouseAction {
/* create the Action*/
- val mouseAction = MouseAction(this as EventType)
- .also { it.keyTracker = this@ActionSet.keyTracker }
+ return MouseAction(this as EventType).apply {
+ keyTracker = this@ActionSet.keyTracker
+
+ /* set the default name */
+ name = this@ActionSet.name
- /* copnfigure based on the parameters */
- mouseAction.apply {
+ /* configure based on the parameters */
mouseButtonTrigger?.let {
/* default to exclusive if pressed, and NOT exclusive if released*/
verifyButtonTrigger(mouseButtonTrigger, released = onRelease, exclusive = !onRelease)
@@ -278,14 +287,9 @@ open class ActionSet(val name: String, var keyTracker: () -> KeyTracker? = { nul
keysDown(*it, exclusive = keysExclusive)
} ?: ignoreKeys()
- }
-
- /* configure based on the callback, and add to ActionSet */
- mouseAction
- .apply(withAction)
- .also { addAction(it) }
-
- return mouseAction
+ /* configure based on the callback */
+ withAction()
+ }.also { addAction(it)}
}
/**
@@ -360,10 +364,14 @@ open class ActionSet(val name: String, var keyTracker: () -> KeyTracker? = { nul
*/
@JvmSynthetic
fun mouseAction(eventType: EventType, withAction: MouseAction.() -> Unit = {}): MouseAction {
- return MouseAction(eventType)
- .also { it.keyTracker = this.keyTracker }
- .apply(withAction)
- .also { addAction(it) }
+ return MouseAction(eventType).apply {
+ keyTracker = this@ActionSet.keyTracker
+
+ /* set the default name */
+ name = this@ActionSet.name
+
+ withAction()
+ }.also { addAction(it) }
}
diff --git a/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/DragActionSet.kt b/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/DragActionSet.kt
index 93b022b..8793a36 100644
--- a/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/DragActionSet.kt
+++ b/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/DragActionSet.kt
@@ -20,16 +20,26 @@ import java.util.function.Consumer
*
* @param name of the [DragActionSet];
* @param keyTracker to provide the key tracker to track the key state
- * @param filter to apply before triggering a drag event (Only [DRAG_DETECTED] and [MOUSE_DRAGGED] are drag events)
+ * @param filter to set drag actions as filters
+ * @param consumeMouseClicked event generated when releasing a drag.
+ * If a drag begins and ends on the same node, it triggers a MOUSE_CLICKED event, regardless of how far the mouse
+ * travelled in between MOUSE_PRESSED and MOUSE_RELEASED. If [consumeMouseClicked] is set to true, then a
+ * MOUSE_CLICKED action listener with consume the resulting MOUSE_CLICKED as a filter prior.
* @param apply configuration callback for the created [DragActionSet]
+ * var consumeMouseClicked = consumeMouseClicked
*/
-open class DragActionSet @JvmOverloads constructor(name: String, keyTracker: () -> KeyTracker? = { null }, filter: Boolean = true, apply: (DragActionSet.() -> Unit)? = null) : ActionSet(name, keyTracker) {
+open class DragActionSet @JvmOverloads constructor(
+ name: String,
+ keyTracker: () -> KeyTracker? = { null },
+ filter: Boolean = true,
+ consumeMouseClicked: Boolean = false,
+ apply: (DragActionSet.() -> Unit)? = null) : ActionSet(name, keyTracker) {
/**
* [DRAG_DETECTED] [Action]. Can be access for further configuration
*/
val dragDetectedAction = DRAG_DETECTED {
- this.name = "${this@DragActionSet.name} (drag detected)"
+ this.name = "${this@DragActionSet.name}.drag detected"
this.filter = filter
verifyEventNotNull()
}
@@ -38,7 +48,7 @@ open class DragActionSet @JvmOverloads constructor(name: String, keyTracker: ()
* [MOUSE_DRAGGED] [Action]. Can be access for further configuration
*/
val dragAction = MOUSE_DRAGGED {
- this.name = "${this@DragActionSet.name} (drag)"
+ this.name = "${this@DragActionSet.name}.drag"
this.filter = filter
verifyEventNotNull()
verify { isDragging }
@@ -48,7 +58,7 @@ open class DragActionSet @JvmOverloads constructor(name: String, keyTracker: ()
* [MOUSE_RELEASED] [Action]. Can be access for further configuration
*/
val dragReleaseAction = MOUSE_RELEASED {
- this.name = "${this@DragActionSet.name} (drag released)"
+ this.name = "${this@DragActionSet.name}.drag released"
this.filter = filter
verifyEventNotNull()
verify { isDragging }
@@ -65,9 +75,10 @@ open class DragActionSet @JvmOverloads constructor(name: String, keyTracker: ()
var startY = 0.0
/**
- * if true, [startX],[startY] will be updated when [MOUSE_DRAGGED]. false by default
+ * if true, [startX],[startY] will be updated when [MOUSE_DRAGGED].
+ * if fals, [startX],[startY] are always the position of the MOUSE_PRESSED event that triggered the drag event.
*/
- var updateXY = false
+ var relative = false
private val readOnlyIsDraggingWrapper = ReadOnlyBooleanWrapper()
@@ -82,11 +93,35 @@ open class DragActionSet @JvmOverloads constructor(name: String, keyTracker: ()
*/
val isDragging: Boolean by readOnlyIsDraggingWrapper.readOnlyProperty.nonnullVal()
+ private var nextClickFromDragRelease = false
+
init {
+ MOUSE_PRESSED {
+ this.name = "${this@DragActionSet.name}.start position for drag threshold"
+ consume = false
+ verifyEventNotNull()
+ verify("start position updated only until the drag is detected") {!isDragging }
+ onAction {
+ if (!relative) {
+ startX = it!!.x
+ startY = it.y
+ }
+ }
+ }
/* Initialize with empty lambda, so only drag state updates occur */
onDragDetected { }
onDrag { }
onDragReleased { }
+ if (consumeMouseClicked) {
+ MOUSE_CLICKED {
+ this.name = "${this@DragActionSet.name}.DragActionSet Mouse Release Consumer"
+ this.filter = true
+ onAction {
+ consume = nextClickFromDragRelease
+ nextClickFromDragRelease = false
+ }
+ }
+ }
apply?.let { it() }
}
@@ -135,6 +170,7 @@ open class DragActionSet @JvmOverloads constructor(name: String, keyTracker: ()
onAction {
endDragState()
onDragReleased(it!!)
+ nextClickFromDragRelease = true
}
}
}
@@ -152,13 +188,12 @@ open class DragActionSet @JvmOverloads constructor(name: String, keyTracker: ()
}
private fun initDragState(it: MouseEvent) {
- startX = it.x
- startY = it.y
_isDragging = true
+ updateDragState(it)
}
private fun updateDragState(it: MouseEvent) {
- if (updateXY) {
+ if (relative) {
startX = it.x
startY = it.y
}
diff --git a/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/KeyAction.kt b/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/KeyAction.kt
index f6483c6..916cb99 100644
--- a/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/KeyAction.kt
+++ b/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/KeyAction.kt
@@ -24,17 +24,21 @@ class KeyAction(eventType: EventType) : Action(eventType) {
* @param keyBindings the map to query the key combinations for the given [keyName]
* @param keyName key used to find the desired key combination in the [keyBindings]
*/
- fun keyMatchesBinding(keyBindings: NamedKeyCombination.CombinationMap, keyName: String) {
+ @JvmOverloads
+ fun keyMatchesBinding(keyBindings: NamedKeyCombination.CombinationMap, keyName: String, keysExclusive: Boolean = true) {
if (name == null) name = keyName
ignoreKeys()
- verify { event ->
- /* always valid here if the event is null; it indicates we are triggering the action programatically, not via an Event */
- event?.let {
- keyBindings.matches(keyName, it).also { match ->
- if (!match) logger.trace("key did not match bindings")
- }
- } ?: true
- }
+ if (eventType == KeyEvent.KEY_RELEASED)
+ keysReleased(*keyBindings[keyName]!!.keyCodes.toTypedArray())
+ else
+ verify { event ->
+ /* always valid here if the event is null; it indicates we are triggering the action programatically, not via an Event */
+ event?.let {
+ keyBindings.matches(keyName, it, keysExclusive).also { match ->
+ if (!match) logger.trace("key did not match bindings")
+ }
+ } ?: true
+ }
}
/**
@@ -86,8 +90,8 @@ class KeyAction(eventType: EventType) : Action(eventType) {
* @receiver the [EventType] to trigger the [KeyAction] on
*/
@JvmSynthetic
- fun > T.action(keyBindings: NamedKeyCombination.CombinationMap, keys: String, action: Action.() -> Unit) = KeyAction(this).also {
- it.keyMatchesBinding(keyBindings, keys)
+ fun > T.action(keyBindings: NamedKeyCombination.CombinationMap, keys: String, keysExclusive: Boolean = true, action: Action.() -> Unit) = KeyAction(this).also {
+ it.keyMatchesBinding(keyBindings, keys, keysExclusive)
it.action()
}
@@ -101,13 +105,14 @@ class KeyAction(eventType: EventType) : Action(eventType) {
* @receiver the [EventType] to trigger the [KeyAction] on
*/
@JvmSynthetic
- fun > T.onAction(keyBindings: NamedKeyCombination.CombinationMap, keys: String, onAction: (KeyEvent?) -> Unit) = KeyAction(this).also { action ->
- action.keyMatchesBinding(keyBindings, keys)
+ fun > T.onAction(keyBindings: NamedKeyCombination.CombinationMap, keys: String, keysExclusive: Boolean = true, onAction: (KeyEvent?) -> Unit) = KeyAction(this).also { action ->
+ action.keyMatchesBinding(keyBindings, keys, keysExclusive)
action.onAction { onAction(it) }
}
@JvmStatic
- fun > T.onAction(keyBindings: NamedKeyCombination.CombinationMap, keys: String, onAction: Consumer) =
- onAction(keyBindings, keys) { onAction.accept(it) }
+ @JvmOverloads
+ fun > T.onAction(keyBindings: NamedKeyCombination.CombinationMap, keys: String, keysExclusive: Boolean = true, onAction: Consumer) =
+ onAction(keyBindings, keys, keysExclusive) { onAction.accept(it) }
}
}
diff --git a/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/MouseCombination.kt b/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/MouseCombination.kt
index 8004155..61b376a 100644
--- a/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/MouseCombination.kt
+++ b/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/MouseCombination.kt
@@ -15,7 +15,7 @@ class MouseCombination private constructor(keyCombination: KeyCombination) {
constructor(keyCode: KeyCode, vararg modifier: KeyCombination.Modifier) : this(KeyCodeCombination(keyCode, *modifier))
- constructor(vararg modifier: KeyCombination.Modifier) : this(OnlyModifierKeyCombination(*modifier))
+ constructor(vararg modifier: KeyCombination.Modifier) : this(NamedKeyCombination.OnlyModifierKeyCombination(*modifier))
private val _keyCombination: ObjectProperty = SimpleObjectProperty(keyCombination)
@@ -24,8 +24,8 @@ class MouseCombination private constructor(keyCombination: KeyCombination) {
set(keyCombination) = setKeyCombinationChecked(keyCombination)
private fun setKeyCombinationChecked(keyCombination: KeyCombination) {
- require(keyCombination is KeyCodeCombination || keyCombination is OnlyModifierKeyCombination) {
- "Currently only ${KeyCodeCombination::class} and ${OnlyModifierKeyCombination::class} are supported but got $keyCombination."
+ require(keyCombination is KeyCodeCombination || keyCombination is NamedKeyCombination.OnlyModifierKeyCombination) {
+ "Currently only ${KeyCodeCombination::class} and ${NamedKeyCombination.OnlyModifierKeyCombination::class} are supported but got $keyCombination."
}
_keyCombination.value = keyCombination
}
@@ -57,8 +57,6 @@ class MouseCombination private constructor(keyCombination: KeyCombination) {
val deepCopy: MouseCombination
get() = MouseCombination(keyCombination)
- class OnlyModifierKeyCombination(vararg modifier: Modifier) : KeyCombination(*modifier)
-
companion object {
private val LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass())
}
diff --git a/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/NamedKeyCombination.kt b/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/NamedKeyCombination.kt
index c5ecbde..ba37dc9 100644
--- a/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/NamedKeyCombination.kt
+++ b/src/main/kotlin/org/janelia/saalfeldlab/fx/actions/NamedKeyCombination.kt
@@ -1,26 +1,48 @@
package org.janelia.saalfeldlab.fx.actions
+import com.sun.javafx.tk.Toolkit
import javafx.beans.property.SimpleObjectProperty
-import javafx.scene.input.KeyCode
-import javafx.scene.input.KeyCodeCombination
-import javafx.scene.input.KeyCombination
-import javafx.scene.input.KeyEvent
+import javafx.scene.input.*
import org.apache.commons.lang.builder.ToStringBuilder
import org.apache.commons.lang.builder.ToStringStyle
import org.janelia.saalfeldlab.fx.extensions.nonnull
import kotlin.collections.set
-class NamedKeyCombination(val name: String, primaryCombination: KeyCodeCombination) {
-
- constructor(name: String, keyCode: KeyCode, vararg modifiers: KeyCombination.Modifier) : this(name, KeyCodeCombination(keyCode, *modifiers))
+private val KeyCombination.modifierCodes: Set
+ get() = setOfNotNull(
+ if (shift == KeyCombination.ModifierValue.DOWN) KeyCode.SHIFT else null,
+ if (control == KeyCombination.ModifierValue.DOWN) KeyCode.CONTROL else null,
+ if (alt == KeyCombination.ModifierValue.DOWN) KeyCode.ALT else null,
+ if (meta == KeyCombination.ModifierValue.DOWN) KeyCode.META else null,
+ if (shortcut == KeyCombination.ModifierValue.DOWN) Toolkit.getToolkit().platformShortcutKey else null
+ )
+
+private val KeyEvent.modifierCodes: Set
+ get() = setOfNotNull(
+ if (isShiftDown) KeyCode.SHIFT else null,
+ if (isControlDown) KeyCode.CONTROL else null,
+ if (isAltDown) KeyCode.ALT else null,
+ if (isMetaDown) KeyCode.META else null,
+ if (isShortcutDown) Toolkit.getToolkit().platformShortcutKey else null
+ )
+
+class NamedKeyCombination(val name: String, primaryCombination: KeyCombination) {
private val primaryCombinationProperty = SimpleObjectProperty(primaryCombination)
- var primaryCombination: KeyCodeCombination by primaryCombinationProperty.nonnull()
+ var primaryCombination: KeyCombination by primaryCombinationProperty.nonnull()
fun primaryCombinationProperty() = primaryCombinationProperty
fun matches(event: KeyEvent) = primaryCombination.match(event)
+ val keyCodes: Set
+ get() {
+ val codes = mutableSetOf()
+ (primaryCombination as? KeyCodeCombination)?.code?.also { codes += it }
+ codes += primaryCombination.modifierCodes
+ return codes.toSet()
+ }
+
val deepCopy: NamedKeyCombination
get() = NamedKeyCombination(name, primaryCombination)
@@ -53,7 +75,16 @@ class NamedKeyCombination(val name: String, primaryCombination: KeyCodeCombinati
this[keyCombination.name] = keyCombination
}
- fun matches(name: String, event: KeyEvent) = get(name)!!.matches(event)
+ fun matches(name: String, event: KeyEvent, keysExclusive : Boolean = true) : Boolean {
+ val namedCombo = get(name)!!
+ return if (keysExclusive) {
+ namedCombo.matches(event)
+ } else {
+ val combo = namedCombo.primaryCombination
+ val codesMatchIfCodeCombo = (combo as? KeyCodeCombination)?.code?.let { it == event.code } ?: true
+ codesMatchIfCodeCombo && event.modifierCodes.containsAll(combo.modifierCodes)
+ }
+ }
operator fun plusAssign(keyCombination: NamedKeyCombination) = addCombination(keyCombination)
@@ -67,4 +98,10 @@ class NamedKeyCombination(val name: String, primaryCombination: KeyCodeCombinati
get() = values.map { it.deepCopy }.toTypedArray().let { CombinationMap(*it) }
}
+ class OnlyModifierKeyCombination(vararg modifier: Modifier) : KeyCombination(*modifier) {
+ override fun match(event: KeyEvent?): Boolean {
+ return super.match(event)
+ }
+ }
+
}
diff --git a/src/main/kotlin/org/janelia/saalfeldlab/fx/event/KeyTracker.kt b/src/main/kotlin/org/janelia/saalfeldlab/fx/event/KeyTracker.kt
index cad9cf0..c8239d2 100644
--- a/src/main/kotlin/org/janelia/saalfeldlab/fx/event/KeyTracker.kt
+++ b/src/main/kotlin/org/janelia/saalfeldlab/fx/event/KeyTracker.kt
@@ -44,6 +44,7 @@ class KeyTracker {
private val actions by lazy {
ActionSet("Key Tracker", { this }) {
KEY_PRESSED {
+ name = "key-pressed"
ignoreKeys()
filter = true
consume = false
@@ -51,6 +52,7 @@ class KeyTracker {
onAction { addKey(it!!.code) }
}
KEY_RELEASED {
+ name = "key-released"
ignoreKeys()
filter = true
consume = false
diff --git a/src/main/kotlin/org/janelia/saalfeldlab/fx/midi/MidiActionSet.kt b/src/main/kotlin/org/janelia/saalfeldlab/fx/midi/MidiActionSet.kt
index b8ce457..0c939e0 100644
--- a/src/main/kotlin/org/janelia/saalfeldlab/fx/midi/MidiActionSet.kt
+++ b/src/main/kotlin/org/janelia/saalfeldlab/fx/midi/MidiActionSet.kt
@@ -46,33 +46,35 @@ open class MidiActionSet(name: String, private val device: MCUControlPanel, priv
@JvmSynthetic
fun potentiometerAction(eventType: EventType, handle: Int, withAction: PotentiometerAction.() -> Unit = {}): PotentiometerAction {
- return PotentiometerAction(eventType, device, handle, withAction).also { addAction(it) }
+ return PotentiometerAction(eventType, device, handle, name, withAction).also { addAction(it) }
}
@JvmSynthetic
fun toggleAction(eventType: EventType, handle: Int, withAction: ToggleAction.() -> Unit = {}): ToggleAction {
- return ToggleAction(eventType, device, handle, withAction).also { addAction(it) }
+ return ToggleAction(eventType, device, handle, name, withAction).also { addAction(it) }
}
@JvmSynthetic
fun buttonAction(eventType: EventType, handle: Int, withAction: ButtonAction.() -> Unit = {}): ButtonAction {
- return ButtonAction(eventType, device, handle, withAction).also { addAction(it) }
+ return ButtonAction(eventType, device, handle, name, withAction).also { addAction(it) }
}
@JvmSynthetic
fun faderAction(eventType: EventType, handle: Int, withAction: FaderAction.() -> Unit = {}): FaderAction {
- return FaderAction(eventType, device, handle, withAction).also { addAction(it) }
+ return FaderAction(eventType, device, handle, name, withAction).also { addAction(it) }
}
}
-abstract class MidiAction(eventType: EventType, val device: MCUControlPanel, val handle: Int, withAction: MidiAction.() -> Unit = {}) : Action(eventType) {
+abstract class MidiAction(eventType: EventType, val device: MCUControlPanel, val handle: Int, name : String? = null, withAction: MidiAction.() -> Unit = {}) : Action(eventType) {
abstract val control: MCUControl
protected abstract var eventFiringListener: IntConsumer?
var supressEvents = false
init {
ignoreKeys()
+ /* set the default name*/
+ name?.let { this.name = it }
apply(withAction)
}
@@ -98,7 +100,7 @@ abstract class MidiAction(eventType: EventType, val device:
}
}
-class PotentiometerAction(eventType: EventType, device: MCUControlPanel, handle: Int, withAction: PotentiometerAction.() -> Unit = {}) : MidiAction(eventType, device, handle) {
+class PotentiometerAction(eventType: EventType, device: MCUControlPanel, handle: Int, name : String? = null, withAction: PotentiometerAction.() -> Unit = {}) : MidiAction(eventType, device, handle, name) {
override val control: MCUVPotControl = device.getVPotControl(handle)
override var eventFiringListener: IntConsumer? = null
@@ -155,7 +157,7 @@ class PotentiometerAction(eventType: EventType, device:
}
}
-class ButtonAction(eventType: EventType, device: MCUControlPanel, handle: Int, withAction: ButtonAction.() -> Unit = {}) : MidiAction(eventType, device, handle) {
+class ButtonAction(eventType: EventType, device: MCUControlPanel, handle: Int, name : String? = null,withAction: ButtonAction.() -> Unit = {}) : MidiAction(eventType, device, handle, name) {
override val control: MCUButtonControl = device.getButtonControl(handle)
override var eventFiringListener: IntConsumer? = null
@@ -186,7 +188,7 @@ class ButtonAction(eventType: EventType, device: MCUControlPane
}
}
-class ToggleAction(eventType: EventType, device: MCUControlPanel, handle: Int, withAction: ToggleAction.() -> Unit = {}) : MidiAction(eventType, device, handle) {
+class ToggleAction(eventType: EventType, device: MCUControlPanel, handle: Int, name : String? = null, withAction: ToggleAction.() -> Unit = {}) : MidiAction(eventType, device, handle, name) {
override val control: MCUButtonControl = device.getButtonControl(handle)
override var eventFiringListener: IntConsumer? = null
@@ -218,7 +220,7 @@ class ToggleAction(eventType: EventType, device: MCUControlPane
}
-class FaderAction(eventType: EventType, device: MCUControlPanel, handle: Int, withAction: FaderAction.() -> Unit = {}) : MidiAction(eventType, device, handle) {
+class FaderAction(eventType: EventType, device: MCUControlPanel, handle: Int, name : String? = null, withAction: FaderAction.() -> Unit = {}) : MidiAction(eventType, device, handle, name) {
override val control: MCUFaderControl = device.getFaderControl(handle)
override var eventFiringListener: IntConsumer? = null
diff --git a/src/main/kotlin/org/janelia/saalfeldlab/fx/ui/ResizeOnLeftSide.kt b/src/main/kotlin/org/janelia/saalfeldlab/fx/ui/ResizeOnLeftSide.kt
index 51bd236..f8702ee 100644
--- a/src/main/kotlin/org/janelia/saalfeldlab/fx/ui/ResizeOnLeftSide.kt
+++ b/src/main/kotlin/org/janelia/saalfeldlab/fx/ui/ResizeOnLeftSide.kt
@@ -64,7 +64,7 @@ class ResizeOnLeftSide @JvmOverloads constructor(
private val mouseDragged = DragActionSet("resize") {
verify { isCurrentlyWithinMarginOfBorder }
- updateXY = false
+ relative = false
dragDetectedAction.filter = true
dragAction.filter = true
diff --git a/src/main/kotlin/org/janelia/saalfeldlab/fx/ui/resize/ResizeHorizontally.kt b/src/main/kotlin/org/janelia/saalfeldlab/fx/ui/resize/ResizeHorizontally.kt
index 7876cdd..2adac78 100644
--- a/src/main/kotlin/org/janelia/saalfeldlab/fx/ui/resize/ResizeHorizontally.kt
+++ b/src/main/kotlin/org/janelia/saalfeldlab/fx/ui/resize/ResizeHorizontally.kt
@@ -73,7 +73,7 @@ class ResizeHorizontally @JvmOverloads constructor(
}
private val mouseDragged = DragActionSet("resize horizontally") {
- updateXY = false
+ relative = false
verify { canResize }
onDrag {
val dx = it.x - currentPosition