diff --git a/VERSION b/VERSION index dd2601c..cdae3ef 100644 --- a/VERSION +++ b/VERSION @@ -1,3 +1,9 @@ +0.2.1 +October 22, 2020 +- COVID lockdown +- Improvements to fixture/structure/JSON system +- Many tweaks and improvements, new patterns/effects + 0.2.0 May 28, 2020 - Lots of API cleanups @@ -6,12 +12,12 @@ May 28, 2020 0.1.2 August 20, 2018 -- Fix bug with onActive/onInactive messages when pattern is deleted. +-Fix bug with onActive/onInactive messages when pattern is deleted. 0.1.1 August 15, 2018 -- Dropping alpha tag, updating release packaging with VERSION and LICENSE files. +-Dropping alpha tag, updating release packaging with VERSION and LICENSE files. 0.1.0-alpha August 13, 2018 -- Initial release cut for tracking versioned changes across the LX repositories. +-Initial release cut for tracking versioned changes across the LX repositories. diff --git a/pom.xml b/pom.xml index 252a4a3..cb4fe6c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ heronarts p3lx - 0.2.0 + 0.2.1 jar diff --git a/src/main/java/heronarts/p3lx/P3LX.java b/src/main/java/heronarts/p3lx/P3LX.java index fa82d57..cfa7dee 100644 --- a/src/main/java/heronarts/p3lx/P3LX.java +++ b/src/main/java/heronarts/p3lx/P3LX.java @@ -132,6 +132,14 @@ public P3LX(PApplet applet, Flags flags, LXModel model) { LX.initProfiler.log("P3LX: registerMethod"); } + @Override + protected void showConfirmUnsavedProjectDialog(String message, Runnable confirm) { + this.ui.showConfirmDialog( + "Your project has unsaved changes, really " + message + "?", + confirm + ); + } + /** * Subclass may override. * diff --git a/src/main/java/heronarts/p3lx/ui/CustomDeviceUI.java b/src/main/java/heronarts/p3lx/ui/CustomDeviceUI.java deleted file mode 100644 index b1070f6..0000000 --- a/src/main/java/heronarts/p3lx/ui/CustomDeviceUI.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright 2013- Mark C. Slee, Heron Arts LLC - * - * This file is part of the LX Studio software library. By using - * LX, you agree to the terms of the LX Studio Software License - * and Distribution Agreement, available at: http://lx.studio/license - * - * Please note that the LX license is not open-source. The license - * allows for free, non-commercial use. - * - * HERON ARTS MAKES NO WARRANTY, EXPRESS, IMPLIED, STATUTORY, OR - * OTHERWISE, AND SPECIFICALLY DISCLAIMS ANY WARRANTY OF - * MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR A PARTICULAR - * PURPOSE, WITH RESPECT TO THE SOFTWARE. - * - * @author Mark C. Slee - */ - -package heronarts.p3lx.ui; - -public interface CustomDeviceUI { - public abstract void buildDeviceUI(UI ui, UI2dContainer device); -} diff --git a/src/main/java/heronarts/p3lx/ui/UI.java b/src/main/java/heronarts/p3lx/ui/UI.java index f67645a..a96d7dd 100644 --- a/src/main/java/heronarts/p3lx/ui/UI.java +++ b/src/main/java/heronarts/p3lx/ui/UI.java @@ -581,7 +581,7 @@ private boolean isMapping() { return this.midiMapping || this.modulationSourceMapping || this.modulationTargetMapping || this.triggerSourceMapping || this.triggerTargetMapping; } - void setMouseoverHelpText(String helpText) { + public void setMouseoverHelpText(String helpText) { if (!isMapping()) { this.contextualHelpText.setValue(helpText); } diff --git a/src/main/java/heronarts/p3lx/ui/UI3dContext.java b/src/main/java/heronarts/p3lx/ui/UI3dContext.java index e265c26..7efcd23 100644 --- a/src/main/java/heronarts/p3lx/ui/UI3dContext.java +++ b/src/main/java/heronarts/p3lx/ui/UI3dContext.java @@ -60,6 +60,7 @@ public class UI3dContext extends UIObject implements LXSerializable, UITabFocus public static final int NUM_CAMERA_POSITIONS = 6; public static interface MovementListener { + public void reset(); public void translate(float x, float y, float z); public void rotate(float theta, float phi); } @@ -216,7 +217,7 @@ public String toString() { */ private final List movementListeners = new ArrayList(); - public final void addMovementistener(MovementListener listener) { + public final void addMovementListener(MovementListener listener) { Objects.requireNonNull(listener, "Cannot add null UI3dContext.MovementListener"); if (this.movementListeners.contains(listener)) { throw new IllegalStateException("Cannot add duplicate UI3dContext.MovementListener: " + listener); @@ -937,6 +938,9 @@ protected void onMousePressed(MouseEvent mouseEvent, float mx, float my) { if (mouseEvent.getCount() > 1) { focus(mouseEvent); } + for (MovementListener listener : this.movementListeners) { + listener.reset(); + } } private void updateFocusedCamera() { diff --git a/src/main/java/heronarts/p3lx/ui/UIKeyEvent.java b/src/main/java/heronarts/p3lx/ui/UIKeyEvent.java new file mode 100644 index 0000000..f3545e2 --- /dev/null +++ b/src/main/java/heronarts/p3lx/ui/UIKeyEvent.java @@ -0,0 +1,49 @@ +/** + * Copyright 2018- Mark C. Slee, Heron Arts LLC + * + * This file is part of the LX Studio software library. By using + * LX, you agree to the terms of the LX Studio Software License + * and Distribution Agreement, available at: http://lx.studio/license + * + * Please note that the LX license is not open-source. The license + * allows for free, non-commercial use. + * + * HERON ARTS MAKES NO WARRANTY, EXPRESS, IMPLIED, STATUTORY, OR + * OTHERWISE, AND SPECIFICALLY DISCLAIMS ANY WARRANTY OF + * MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR A PARTICULAR + * PURPOSE, WITH RESPECT TO THE SOFTWARE. + * + * ##library.name## + * ##library.sentence## + * ##library.url## + * + * @author ##author## + * @modified ##date## + * @version ##library.prettyVersion## (##library.version##) + */ + +package heronarts.p3lx.ui; + +import processing.core.PConstants; +import processing.event.KeyEvent; + +public class UIKeyEvent { + // Should never be instantiated + private UIKeyEvent() {} + + public static boolean isCommand(KeyEvent keyEvent) { + return keyEvent.isMetaDown() || keyEvent.isControlDown(); + } + + public static boolean isDelete(KeyEvent keyEvent, int keyCode) { + // NOTE(mcslee): there is serious hackiness under here, P3D surface uses + // JOGL key codes, and Processing remaps the character values but not the + // key codes... + // + // See PSurfaceJOGL hackToChar: + // https://github.com/processing/processing/blob/4cc297c66908899cd29480c202536ecf749854e8/core/src/processing/opengl/PSurfaceJOGL.java#L1187 + return + (keyCode == java.awt.event.KeyEvent.VK_BACK_SPACE) || + (keyEvent.getKey() == PConstants.DELETE); + } +} diff --git a/src/main/java/heronarts/p3lx/ui/UIMouseEvent.java b/src/main/java/heronarts/p3lx/ui/UIMouseEvent.java new file mode 100644 index 0000000..e50d87f --- /dev/null +++ b/src/main/java/heronarts/p3lx/ui/UIMouseEvent.java @@ -0,0 +1,43 @@ +/** + * Copyright 2018- Mark C. Slee, Heron Arts LLC + * + * This file is part of the LX Studio software library. By using + * LX, you agree to the terms of the LX Studio Software License + * and Distribution Agreement, available at: http://lx.studio/license + * + * Please note that the LX license is not open-source. The license + * allows for free, non-commercial use. + * + * HERON ARTS MAKES NO WARRANTY, EXPRESS, IMPLIED, STATUTORY, OR + * OTHERWISE, AND SPECIFICALLY DISCLAIMS ANY WARRANTY OF + * MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR A PARTICULAR + * PURPOSE, WITH RESPECT TO THE SOFTWARE. + * + * ##library.name## + * ##library.sentence## + * ##library.url## + * + * @author ##author## + * @modified ##date## + * @version ##library.prettyVersion## (##library.version##) + */ + +package heronarts.p3lx.ui; + +import processing.event.MouseEvent; + +public class UIMouseEvent { + private UIMouseEvent() {} + + public static boolean isCommand(MouseEvent mouseEvent) { + return mouseEvent.isMetaDown() || mouseEvent.isControlDown(); + } + + public static boolean isMultiSelect(MouseEvent mouseEvent) { + return mouseEvent.isMetaDown() || mouseEvent.isControlDown(); + } + + public static boolean isRangeSelect(MouseEvent mouseEvent) { + return mouseEvent.isShiftDown(); + } +} diff --git a/src/main/java/heronarts/p3lx/ui/component/UIButton.java b/src/main/java/heronarts/p3lx/ui/component/UIButton.java index 00326e2..d0e54fe 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UIButton.java +++ b/src/main/java/heronarts/p3lx/ui/component/UIButton.java @@ -50,11 +50,21 @@ public Action(float w, float h) { this(0, 0, w, h); } + public Action(float w, float h, String label) { + this(0, 0, w, h, label); + } + public Action(float x, float y, float w, float h) { super(x, y, w, h); setBorderRounding(8); setMomentary(true); } + + public Action(float x, float y, float w, float h, String label) { + this(x, y, w, h); + setLabel(label); + } + } public static class Trigger extends UIButton { @@ -62,6 +72,8 @@ public static class Trigger extends UIButton { public static final int HEIGHT = 12; public static final int WIDTH = 16; + private LXParameter controlTarget = null; + public Trigger(UI ui, float x, float y) { this(ui, null, x, y); } @@ -75,6 +87,19 @@ public Trigger(UI ui, BooleanParameter trigger, float x, float y) { setParameter(trigger); } } + + public Trigger setControlTarget(LXParameter controlTarget) { + this.controlTarget = controlTarget; + return this; + } + + @Override + public LXParameter getControlTarget() { + if (this.controlTarget != null) { + return this.controlTarget; + } + return super.getControlTarget(); + } } protected boolean active = false; @@ -379,13 +404,21 @@ protected UIButton setActive(boolean active, boolean pushToParameter) { if (pushToParameter) { if (this.enumParameter != null) { if (active) { - getLX().command.perform(new LXCommand.Parameter.Increment(this.enumParameter)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.Increment(this.enumParameter)); + } else { + this.enumParameter.increment(); + } } } else if (this.booleanParameter != null) { if (this.isMomentary) { this.booleanParameter.setValue(active); } else { - getLX().command.perform(new LXCommand.Parameter.SetNormalized(this.booleanParameter, active)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.SetNormalized(this.booleanParameter, active)); + } else { + this.booleanParameter.setValue(active); + } } } diff --git a/src/main/java/heronarts/p3lx/ui/component/UIButtonGroup.java b/src/main/java/heronarts/p3lx/ui/component/UIButtonGroup.java index 7fabe57..e760df3 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UIButtonGroup.java +++ b/src/main/java/heronarts/p3lx/ui/component/UIButtonGroup.java @@ -69,9 +69,17 @@ public UIButtonGroup(final DiscreteParameter parameter, float x, float y, float public void onToggle(boolean enabled) { if (!inParameterUpdate) { if (enabled) { - getLX().command.perform(new LXCommand.Parameter.SetValue(parameter, iv)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.SetValue(parameter, iv)); + } else { + parameter.setValue(iv); + } } else if (hideFirst) { - getLX().command.perform(new LXCommand.Parameter.SetValue(parameter, 0)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.SetValue(parameter, 0)); + } else { + parameter.setValue(0); + } } } } diff --git a/src/main/java/heronarts/p3lx/ui/component/UICheckbox.java b/src/main/java/heronarts/p3lx/ui/component/UICheckbox.java new file mode 100644 index 0000000..4571e25 --- /dev/null +++ b/src/main/java/heronarts/p3lx/ui/component/UICheckbox.java @@ -0,0 +1,262 @@ +/** + * Copyright 2013- Mark C. Slee, Heron Arts LLC + * + * This file is part of the LX Studio software library. By using + * LX, you agree to the terms of the LX Studio Software License + * and Distribution Agreement, available at: http://lx.studio/license + * + * Please note that the LX license is not open-source. The license + * allows for free, non-commercial use. + * + * HERON ARTS MAKES NO WARRANTY, EXPRESS, IMPLIED, STATUTORY, OR + * OTHERWISE, AND SPECIFICALLY DISCLAIMS ANY WARRANTY OF + * MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR A PARTICULAR + * PURPOSE, WITH RESPECT TO THE SOFTWARE. + * + * ##library.name## + * ##library.sentence## + * ##library.url## + * + * @author ##author## + * @modified ##date## + * @version ##library.prettyVersion## (##library.version##) + */ + +package heronarts.p3lx.ui.component; + +import java.util.Objects; + +import heronarts.lx.command.LXCommand; +import heronarts.lx.parameter.BooleanParameter; +import heronarts.lx.parameter.LXListenableNormalizedParameter; +import heronarts.lx.parameter.LXParameter; +import heronarts.lx.parameter.LXParameterListener; +import heronarts.p3lx.ui.UI; +import heronarts.p3lx.ui.UIFocus; +import heronarts.p3lx.ui.UITriggerSource; +import heronarts.p3lx.ui.UITriggerTarget; +import heronarts.p3lx.ui.UIControlTarget; +import processing.core.PGraphics; +import processing.event.KeyEvent; +import processing.event.MouseEvent; + +public class UICheckbox extends UIParameterComponent implements UIControlTarget, UITriggerSource, UITriggerTarget, UIFocus { + + public final static int DEFAULT_WIDTH = 10; + public final static int DEFAULT_HEIGHT = 10; + + protected boolean active = false; + protected boolean isMomentary = false; + + private boolean triggerable = false; + + protected boolean enabled = true; + + private BooleanParameter parameter = null; + + private final LXParameterListener parameterListener = (p) -> { + setActive(this.parameter.isOn(), false); + }; + + public UICheckbox() { + this(0, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT); + } + + public UICheckbox(float w, BooleanParameter p) { + this(0, 0, w, w, p); + } + + public UICheckbox(float x, float y) { + this(x, y, DEFAULT_WIDTH, DEFAULT_HEIGHT); + } + + public UICheckbox(float x, float y, BooleanParameter p) { + this(x, y, DEFAULT_WIDTH, DEFAULT_HEIGHT, p); + } + + public UICheckbox(float x, float y, float w, float h) { + this(x, y, w, h, null); + } + + public UICheckbox(float x, float y, float w, float h, BooleanParameter p) { + super(x, y, w, h); + setParameter(p); + } + + public UICheckbox setEnabled(boolean enabled) { + if (this.enabled != enabled) { + this.enabled = enabled; + redraw(); + } + return this; + } + + public UICheckbox setTriggerable(boolean triggerable) { + this.triggerable = triggerable; + return this; + } + + @Override + public String getDescription() { + if (this.parameter != null) { + return UIParameterControl.getDescription(this.parameter); + } + return super.getDescription(); + } + + @Override + public LXListenableNormalizedParameter getParameter() { + return this.parameter; + } + + public UICheckbox removeParameter() { + if (this.parameter != null) { + this.parameter.removeListener(this.parameterListener); + } + this.parameter = null; + return this; + } + + public UICheckbox setParameter(BooleanParameter parameter) { + Objects.requireNonNull(parameter, "Cannot set null UICheckbox.setParameter() - use removeParameter() instead"); + if (parameter != this.parameter) { + removeParameter(); + if (parameter != null) { + this.parameter = parameter; + this.parameter.addListener(this.parameterListener); + setMomentary(this.parameter.getMode() == BooleanParameter.Mode.MOMENTARY); + setActive(this.parameter.isOn(), false); + } + } + return this; + } + + public UICheckbox setMomentary(boolean momentary) { + this.isMomentary = momentary; + return this; + } + + @Override + protected void onDraw(UI ui, PGraphics pg) { + // A lighter gray background color when the button is disabled, or it's engaged + // with a mouse press but the mouse has moved off the active button + int color = this.enabled ? ui.theme.getControlTextColor() : ui.theme.getControlDisabledColor(); + + pg.noFill(); + pg.stroke(color); + pg.rect(1, 1, this.width-3, this.height-3); + + if (this.active) { + pg.noStroke(); + pg.fill(color); + pg.rect(3, 3, this.width - 6, this.height - 6); + } + } + + @Override + protected void onMousePressed(MouseEvent mouseEvent, float mx, float my) { + if (this.enabled) { + setActive(this.isMomentary ? true : !this.active); + } + } + + @Override + protected void onMouseReleased(MouseEvent mouseEvent, float mx, float my) { + if (this.enabled) { + if (this.isMomentary) { + setActive(false); + } + } + } + + @Override + protected void onKeyPressed(KeyEvent keyEvent, char keyChar, int keyCode) { + if ((keyCode == java.awt.event.KeyEvent.VK_SPACE) || (keyCode == java.awt.event.KeyEvent.VK_ENTER)) { + if (this.enabled) { + setActive(this.isMomentary ? true : !this.active); + } + consumeKeyEvent(); + } + } + + @Override + protected void onKeyReleased(KeyEvent keyEvent, char keyChar, int keyCode) { + if ((keyCode == java.awt.event.KeyEvent.VK_SPACE) || (keyCode == java.awt.event.KeyEvent.VK_ENTER)) { + consumeKeyEvent(); + if (this.enabled && this.isMomentary) { + setActive(false); + } + } + } + + public boolean isActive() { + return this.active; + } + + public UICheckbox setActive(boolean active) { + return setActive(active, true); + } + + protected UICheckbox setActive(boolean active, boolean pushToParameter) { + if (this.active != active) { + this.active = active; + if (pushToParameter) { + if (this.parameter != null) { + if (this.isMomentary) { + this.parameter.setValue(active); + } else { + getLX().command.perform(new LXCommand.Parameter.SetNormalized(this.parameter, active)); + } + } + } + onToggle(active); + redraw(); + } + return this; + } + + public UICheckbox toggle() { + return setActive(!this.active); + } + + /** + * Subclasses may override this to handle changes to the button's state + * + * @param active Whether button is active + */ + protected void onToggle(boolean active) { + } + + + @Override + public LXParameter getControlTarget() { + if (isMappable()) { + if (this.parameter != null) { + if (this.parameter.getParent() != null) { + return this.parameter; + } + } else { + return getTriggerParameter(); + } + } + return null; + } + + @Override + public BooleanParameter getTriggerSource() { + return this.triggerable ? getTriggerParameter() : null; + } + + @Override + public BooleanParameter getTriggerTarget() { + return this.triggerable ? getTriggerParameter() : null; + } + + private BooleanParameter getTriggerParameter() { + if (this.parameter != null && this.parameter.isMappable() && this.parameter.getParent() != null) { + return this.parameter; + } + return null; + } + +} diff --git a/src/main/java/heronarts/p3lx/ui/component/UICollapsibleSection.java b/src/main/java/heronarts/p3lx/ui/component/UICollapsibleSection.java index 29c9756..dbc4537 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UICollapsibleSection.java +++ b/src/main/java/heronarts/p3lx/ui/component/UICollapsibleSection.java @@ -108,16 +108,12 @@ public void onDraw(UI ui, PGraphics pg) { pg.fill(0xff333333); pg.rect(width-16, PADDING, 12, 12, 4); pg.fill(ui.theme.getControlTextColor()); - if (this.expanded) { - pg.beginShape(); - pg.vertex(this.width-7, 8.5f); - pg.vertex(this.width-13, 8.5f); - pg.vertex(this.width-10, 12.5f); - pg.endShape(PConstants.CLOSE); - } else { - pg.ellipseMode(PConstants.CENTER); - pg.ellipse(width-10, 10, 4, 4); + + pg.rect(width-13, 9, 6, 2); + if (!this.expanded) { + pg.rect(width-11, 7, 2, 6); } + } /** diff --git a/src/main/java/heronarts/p3lx/ui/component/UIColorPicker.java b/src/main/java/heronarts/p3lx/ui/component/UIColorPicker.java index c6b7b0c..623770b 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UIColorPicker.java +++ b/src/main/java/heronarts/p3lx/ui/component/UIColorPicker.java @@ -48,6 +48,8 @@ public enum Corner { private UIColorOverlay uiColorOverlay = null; + private boolean enabled = true; + public UIColorPicker(ColorParameter color) { this(UIKnob.WIDTH, UIKnob.WIDTH, color); } @@ -82,6 +84,11 @@ protected void run() { } } + public UIColorPicker setEnabled(boolean enabled) { + this.enabled = enabled; + return this; + } + private final LXParameterListener redrawSwatch = (p) -> { if (this.uiColorOverlay != null) { this.uiColorOverlay.swatch.redraw(); @@ -139,20 +146,24 @@ private void showOverlay() { @Override public void onMousePressed(MouseEvent mouseEvent, float mx, float my) { - consumeMousePress(); - showOverlay(); + if (this.enabled) { + consumeMousePress(); + showOverlay(); + } super.onMousePressed(mouseEvent, mx, my); } @Override public void onKeyPressed(KeyEvent keyEvent, char keyChar, int keyCode) { - if (keyCode == java.awt.event.KeyEvent.VK_ENTER || keyCode == java.awt.event.KeyEvent.VK_SPACE) { - consumeKeyEvent(); - showOverlay(); - } else if (keyCode == java.awt.event.KeyEvent.VK_ESCAPE) { - if ((this.uiColorOverlay != null) && (this.uiColorOverlay.isVisible())) { + if (this.enabled) { + if (keyCode == java.awt.event.KeyEvent.VK_ENTER || keyCode == java.awt.event.KeyEvent.VK_SPACE) { consumeKeyEvent(); - hideOverlay(); + showOverlay(); + } else if (keyCode == java.awt.event.KeyEvent.VK_ESCAPE) { + if ((this.uiColorOverlay != null) && (this.uiColorOverlay.isVisible())) { + consumeKeyEvent(); + hideOverlay(); + } } } super.onKeyPressed(keyEvent, keyChar, keyCode); diff --git a/src/main/java/heronarts/p3lx/ui/component/UIDoubleBox.java b/src/main/java/heronarts/p3lx/ui/component/UIDoubleBox.java index b218003..d6ed6f2 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UIDoubleBox.java +++ b/src/main/java/heronarts/p3lx/ui/component/UIDoubleBox.java @@ -65,8 +65,16 @@ public UIDoubleBox(float w, BoundedParameter parameter) { this(0, 0, w, parameter); } + public UIDoubleBox(float w, float h, BoundedParameter parameter) { + this(0, 0, w, h, parameter); + } + public UIDoubleBox(float x, float y, float w, BoundedParameter parameter) { - this(x, y, w, DEFAULT_HEIGHT); + this(x, y, w, DEFAULT_HEIGHT, parameter); + } + + public UIDoubleBox(float x, float y, float w, float h, BoundedParameter parameter) { + this(x, y, w, h); setParameter(parameter); } diff --git a/src/main/java/heronarts/p3lx/ui/component/UIDropMenu.java b/src/main/java/heronarts/p3lx/ui/component/UIDropMenu.java index e641370..4601085 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UIDropMenu.java +++ b/src/main/java/heronarts/p3lx/ui/component/UIDropMenu.java @@ -107,7 +107,11 @@ public UIDropMenu setParameter(DiscreteParameter parameter) { this.actions[i] = new UIContextActions.Action(String.valueOf(i)) { @Override public void onContextAction(UI ui) { - getLX().command.perform(new LXCommand.Parameter.SetValue(parameter, ii)); + if (useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.SetValue(parameter, ii)); + } else { + parameter.setValue(ii); + } } }; } @@ -214,10 +218,18 @@ public void onKeyPressed(KeyEvent keyEvent, char keyChar, int keyCode) { toggleExpanded(); } else if (keyCode == java.awt.event.KeyEvent.VK_DOWN) { consumeKeyEvent(); - getLX().command.perform(new LXCommand.Parameter.Increment(this.parameter)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.Increment(this.parameter)); + } else { + this.parameter.increment(); + } } else if (keyCode == java.awt.event.KeyEvent.VK_UP) { consumeKeyEvent(); - getLX().command.perform(new LXCommand.Parameter.Decrement(this.parameter)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.Decrement(this.parameter)); + } else { + this.parameter.decrement(); + } } else if (keyCode == java.awt.event.KeyEvent.VK_ESCAPE) { if (this.contextMenu.isVisible()) { consumeKeyEvent(); diff --git a/src/main/java/heronarts/p3lx/ui/component/UIDynamicColorPicker.java b/src/main/java/heronarts/p3lx/ui/component/UIDynamicColorPicker.java index 4918724..a688b92 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UIDynamicColorPicker.java +++ b/src/main/java/heronarts/p3lx/ui/component/UIDynamicColorPicker.java @@ -25,6 +25,7 @@ import heronarts.p3lx.ui.UI2dComponent; import heronarts.p3lx.ui.UI2dContainer; import heronarts.p3lx.ui.UIFocus; +import heronarts.p3lx.ui.UIKeyEvent; import heronarts.p3lx.ui.UITimerTask; import processing.core.PConstants; import processing.core.PGraphics; @@ -62,7 +63,7 @@ protected void drawFocus(UI ui, PGraphics pg) { @Override public void onKeyPressed(KeyEvent keyEvent, char keyChar, int keyCode) { - if (keyCode == java.awt.event.KeyEvent.VK_BACK_SPACE || keyCode == java.awt.event.KeyEvent.VK_DELETE) { + if (UIKeyEvent.isDelete(keyEvent, keyCode)) { if (this.dynamicColor.getIndex() > 0) { consumeKeyEvent(); hideOverlay(); diff --git a/src/main/java/heronarts/p3lx/ui/component/UIInputBox.java b/src/main/java/heronarts/p3lx/ui/component/UIInputBox.java index 1ade6c1..6e8d1f0 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UIInputBox.java +++ b/src/main/java/heronarts/p3lx/ui/component/UIInputBox.java @@ -54,6 +54,7 @@ public interface ProgressIndicator { protected boolean returnKeyEdit = true; + private boolean mousePressEdit = false; private boolean immediateEdit = false; private ProgressIndicator progressMeter; @@ -134,6 +135,11 @@ public UIInputBox enableImmediateEdit(boolean immediateEdit) { return this; } + public UIInputBox enableMousePressEdit(boolean mousePressEdit) { + this.mousePressEdit = mousePressEdit; + return this; + } + protected abstract String getValueString(); protected abstract void saveEditBuffer(); @@ -345,7 +351,11 @@ protected void setValueCommand(double value) { if (this.mouseDragSetValue != null) { getLX().command.perform(this.mouseDragSetValue.update(value)); } else if (getParameter() != null){ - getLX().command.perform(new LXCommand.Parameter.SetValue(getParameter(), value)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.SetValue(getParameter(), value)); + } else { + getParameter().setValue(value); + } } } @@ -355,7 +365,13 @@ protected void onMousePressed(MouseEvent mouseEvent, float mx, float my) { this.dAccum = 0; LXParameter parameter = getParameter(); if (parameter != null) { - this.mouseDragSetValue = new LXCommand.Parameter.SetValue(parameter, 0); + if (this.useCommandEngine) { + this.mouseDragSetValue = new LXCommand.Parameter.SetValue(parameter, 0); + } + } + if (this.enabled && this.editable && !this.editing && this.mousePressEdit) { + edit(); + redraw(); } } diff --git a/src/main/java/heronarts/p3lx/ui/component/UIIntegerBox.java b/src/main/java/heronarts/p3lx/ui/component/UIIntegerBox.java index 787e2a7..bd655f2 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UIIntegerBox.java +++ b/src/main/java/heronarts/p3lx/ui/component/UIIntegerBox.java @@ -43,10 +43,8 @@ public class UIIntegerBox extends UINumberBox implements UIControlTarget { protected DiscreteParameter parameter = null; protected int editMultiplier = 1; - private final LXParameterListener parameterListener = new LXParameterListener() { - public void onParameterChanged(LXParameter p) { - setValue(parameter.getValuei(), false); - } + private final LXParameterListener parameterListener = (p) -> { + setValue(this.parameter.getValuei(), false); }; public UIIntegerBox() { @@ -71,6 +69,11 @@ public String getDescription() { return UIParameterControl.getDescription(this.parameter); } + @Override + public LXParameter getParameter() { + return this.parameter; + } + public UIIntegerBox setParameter(final DiscreteParameter parameter) { if (this.parameter != parameter) { if (this.parameter != null) { @@ -150,7 +153,11 @@ protected UIIntegerBox setValue(int value, boolean pushToParameter) { } this.value = min + (value - min) % range; if (this.parameter != null && pushToParameter) { - getLX().command.perform(new LXCommand.Parameter.SetValue(this.parameter, this.value)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.SetValue(this.parameter, this.value)); + } else { + this.parameter.setValue(this.value); + } } this.onValueChange(this.value); redraw(); diff --git a/src/main/java/heronarts/p3lx/ui/component/UIItemList.java b/src/main/java/heronarts/p3lx/ui/component/UIItemList.java index f2d189e..d03aff5 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UIItemList.java +++ b/src/main/java/heronarts/p3lx/ui/component/UIItemList.java @@ -35,6 +35,7 @@ import heronarts.p3lx.ui.UI2dContainer; import heronarts.p3lx.ui.UI2dScrollContext; import heronarts.p3lx.ui.UIFocus; +import heronarts.p3lx.ui.UIKeyEvent; import processing.core.PConstants; import processing.core.PGraphics; import processing.event.Event; @@ -813,7 +814,7 @@ private boolean onKeyPressed(KeyEvent keyEvent, char keyChar, int keyCode) { } activate(); } - } else if (keyCode == java.awt.event.KeyEvent.VK_BACK_SPACE) { + } else if (UIKeyEvent.isDelete(keyEvent, keyCode)) { consume = true; delete(); } else if (keyEvent.isControlDown() || keyEvent.isMetaDown()) { diff --git a/src/main/java/heronarts/p3lx/ui/component/UIKnob.java b/src/main/java/heronarts/p3lx/ui/component/UIKnob.java index ae49049..5595435 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UIKnob.java +++ b/src/main/java/heronarts/p3lx/ui/component/UIKnob.java @@ -197,9 +197,17 @@ protected void onMousePressed(MouseEvent mouseEvent, float mx, float my) { if (isEditable() && (this.parameter != null) && (mouseEvent.getCount() > 1)) { LXCompoundModulation modulation = getModulation(mouseEvent.isShiftDown()); if (modulation != null && (mouseEvent.isControlDown() || mouseEvent.isMetaDown())) { - getLX().command.perform(new LXCommand.Parameter.Reset(modulation.range)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.Reset(modulation.range)); + } else { + modulation.range.reset(); + } } else { - getLX().command.perform(new LXCommand.Parameter.Reset(this.parameter)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.Reset(this.parameter)); + } else { + this.parameter.reset(); + } } } } diff --git a/src/main/java/heronarts/p3lx/ui/component/UIParameterComponent.java b/src/main/java/heronarts/p3lx/ui/component/UIParameterComponent.java index 0cd79a0..045409a 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UIParameterComponent.java +++ b/src/main/java/heronarts/p3lx/ui/component/UIParameterComponent.java @@ -16,13 +16,13 @@ public abstract class UIParameterComponent extends UI2dComponent implements UICo public static final float DEFAULT_HEIGHT = 16; + protected boolean useCommandEngine = true; + protected UIParameterComponent(float x, float y, float w, float h) { super(x, y, w, h); } - public LXParameter getParameter() { - return null; - } + public abstract LXParameter getParameter(); public String getOscAddress() { LXParameter parameter = getParameter(); @@ -32,6 +32,11 @@ public String getOscAddress() { return null; } + public UIParameterComponent setUseCommandEngine(boolean useCommandEngine) { + this.useCommandEngine = useCommandEngine; + return this; + } + @Override public List getContextActions() { List actions = new ArrayList(); @@ -53,7 +58,9 @@ protected void onMousePressed(MouseEvent mouseEvent, float mx, float my) { super.onMousePressed(mouseEvent, mx, my); LXParameter parameter = getParameter(); if (parameter != null && parameter instanceof LXNormalizedParameter) { - this.mouseEditCommand = new LXCommand.Parameter.SetNormalized((LXNormalizedParameter) parameter); + if (this.useCommandEngine) { + this.mouseEditCommand = new LXCommand.Parameter.SetNormalized((LXNormalizedParameter) parameter); + } } } @@ -69,7 +76,11 @@ protected void setNormalizedCommand(double newValue) { } else { LXParameter parameter = getParameter(); if (parameter != null && parameter instanceof LXNormalizedParameter) { - getLX().command.perform(new LXCommand.Parameter.SetNormalized((LXNormalizedParameter) parameter, newValue)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.SetNormalized((LXNormalizedParameter) parameter, newValue)); + } else { + ((LXNormalizedParameter) parameter).setNormalized(newValue); + } } } } diff --git a/src/main/java/heronarts/p3lx/ui/component/UIParameterControl.java b/src/main/java/heronarts/p3lx/ui/component/UIParameterControl.java index 0282cfa..5cd252f 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UIParameterControl.java +++ b/src/main/java/heronarts/p3lx/ui/component/UIParameterControl.java @@ -46,6 +46,7 @@ import heronarts.p3lx.ui.UI; import heronarts.p3lx.ui.UIControlTarget; import heronarts.p3lx.ui.UICopy; +import heronarts.p3lx.ui.UIKeyEvent; import heronarts.p3lx.ui.UIModulationSource; import heronarts.p3lx.ui.UIModulationTarget; import heronarts.p3lx.ui.UIPaste; @@ -262,7 +263,11 @@ protected void saveEditBuffer() { for (String part : parts) { value = value * 60 + Double.parseDouble(part); } - getLX().command.perform(new LXCommand.Parameter.SetValue(this.parameter, value * multiplier)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.SetValue(this.parameter, value * multiplier)); + } else { + this.parameter.setValue(value * multiplier); + } break; default: // No colon character allowed for other types @@ -270,7 +275,11 @@ protected void saveEditBuffer() { } } else { double value = Double.parseDouble(this.editBuffer); - getLX().command.perform(new LXCommand.Parameter.SetValue(this.parameter, value)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.SetValue(this.parameter, value)); + } else { + this.parameter.setValue(value); + } } } catch (NumberFormatException nfx) {} } @@ -317,9 +326,17 @@ protected void decrementValue(KeyEvent keyEvent) { consumeKeyEvent(); if (this.parameter instanceof DiscreteParameter) { DiscreteParameter dp = (DiscreteParameter) this.parameter; - getLX().command.perform(new LXCommand.Parameter.Decrement(dp, keyEvent.isShiftDown() ? dp.getRange() / 10 : 1)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.Decrement(dp, keyEvent.isShiftDown() ? dp.getRange() / 10 : 1)); + } else { + dp.decrement(keyEvent.isShiftDown() ? dp.getRange() / 10 : 1); + } } else if (this.parameter instanceof BooleanParameter) { - getLX().command.perform(new LXCommand.Parameter.SetNormalized((BooleanParameter) this.parameter, false)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.SetNormalized((BooleanParameter) this.parameter, false)); + } else { + ((BooleanParameter) this.parameter).setValue(false); + } } else { setNormalized(getNormalized() - getIncrement(keyEvent)); } @@ -338,9 +355,17 @@ protected void incrementValue(KeyEvent keyEvent) { consumeKeyEvent(); if (this.parameter instanceof DiscreteParameter) { DiscreteParameter dp = (DiscreteParameter) this.parameter; - getLX().command.perform(new LXCommand.Parameter.Increment(dp, keyEvent.isShiftDown() ? dp.getRange() / 10 : 1)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.Increment(dp, keyEvent.isShiftDown() ? dp.getRange() / 10 : 1)); + } else { + dp.increment(keyEvent.isShiftDown() ? dp.getRange() / 10 : 1); + } } else if (this.parameter instanceof BooleanParameter) { - getLX().command.perform(new LXCommand.Parameter.SetNormalized((BooleanParameter) this.parameter, true)); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.SetNormalized((BooleanParameter) this.parameter, true)); + } else { + ((BooleanParameter) this.parameter).setValue(true); + } } else { setNormalized(getNormalized() + getIncrement(keyEvent)); } @@ -353,7 +378,7 @@ protected void onKeyPressed(KeyEvent keyEvent, char keyChar, int keyCode) { if ((keyCode == java.awt.event.KeyEvent.VK_SPACE) || (keyCode == java.awt.event.KeyEvent.VK_ENTER)) { consumeKeyEvent(); setShowValue(true); - } else if (isEnabled() && isEditable() && keyEvent.isShiftDown() && keyCode == java.awt.event.KeyEvent.VK_BACK_SPACE) { + } else if (isEnabled() && isEditable() && keyEvent.isShiftDown() && UIKeyEvent.isDelete(keyEvent, keyCode)) { consumeKeyEvent(); if (this.parameter != null) { this.parameter.reset(); diff --git a/src/main/java/heronarts/p3lx/ui/component/UISwitch.java b/src/main/java/heronarts/p3lx/ui/component/UISwitch.java index 794b7e9..e552795 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UISwitch.java +++ b/src/main/java/heronarts/p3lx/ui/component/UISwitch.java @@ -106,7 +106,11 @@ protected void onKeyPressed(KeyEvent keyEvent, char keyChar, int keyCode) { if (this.isMomentary) { getBooleanParameter().setValue(true); } else { - getLX().command.perform(new LXCommand.Parameter.Toggle(getBooleanParameter())); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.Toggle(getBooleanParameter())); + } else { + getBooleanParameter().toggle(); + } } } } @@ -137,7 +141,11 @@ protected void onMousePressed(MouseEvent mouseEvent, float mx, float my) { if (this.isMomentary) { getBooleanParameter().setValue(true); } else { - getLX().command.perform(new LXCommand.Parameter.Toggle(getBooleanParameter())); + if (this.useCommandEngine) { + getLX().command.perform(new LXCommand.Parameter.Toggle(getBooleanParameter())); + } else { + getBooleanParameter().toggle(); + } } } } diff --git a/src/main/java/heronarts/p3lx/ui/component/UITextBox.java b/src/main/java/heronarts/p3lx/ui/component/UITextBox.java index c5e7fb2..e04c197 100644 --- a/src/main/java/heronarts/p3lx/ui/component/UITextBox.java +++ b/src/main/java/heronarts/p3lx/ui/component/UITextBox.java @@ -40,10 +40,8 @@ public class UITextBox extends UIInputBox implements UICopy, UIPaste { private String value = NO_VALUE; private StringParameter parameter = null; - private final LXParameterListener parameterListener = new LXParameterListener() { - public void onParameterChanged(LXParameter p) { - setValue(parameter.getString(), false); - } + private final LXParameterListener parameterListener = (p) -> { + setValue(this.parameter.getString(), false); }; public UITextBox() { @@ -54,6 +52,11 @@ public UITextBox(float x, float y, float w, float h) { super(x, y, w, h); } + @Override + public LXParameter getParameter() { + return this.parameter; + } + public UITextBox setParameter(StringParameter parameter) { if (this.parameter != null) { this.parameter.removeListener(this.parameterListener); @@ -95,7 +98,11 @@ public UITextBox setValue(String value, boolean pushToParameter) { if (!this.value.equals(value)) { this.value = value; if (pushToParameter && (this.parameter != null)) { - getUI().lx.command.perform(new LXCommand.Parameter.SetString(this.parameter, value)); + if (this.useCommandEngine) { + getUI().lx.command.perform(new LXCommand.Parameter.SetString(this.parameter, value)); + } else { + this.parameter.setValue(value); + } } this.onValueChange(this.value); redraw();