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