From b4ff5114bdcf214b3cd5dd8dcdf2271676bc946e Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Wed, 8 Dec 2021 18:06:30 +0300 Subject: [PATCH] Refactored input event pipeline - Merged Input functionality into InputEvent; removed Input - InputEvents can now be consumed directly - Removed Input.Target; functionality moved into InputBus - Refactored GUI input event handling - Optimized and reduced recursion by unrolling recursion - All input events are now delivered to all components - Filtering occurs at listener level - Refactored InputBus - Listeners may override former Input.Target logic - Improved registration method - Added exception handling - Improved KeyMatcher - Removed builder in favour of copying with modifications - Added String parsing - Added KeyMatcher.matchesIgnoringAction() - Added integration with InputBus - Renamed Component.{add,remove}Listener methods about InputListeners to Component.{add,remove}InputListener to avoid confusion --- .../comms/controls/InputBasedControls.java | 8 +- .../progressia/client/graphics/GUI.java | 55 +-- .../progressia/client/graphics/Layer.java | 4 +- .../graphics/backend/GraphicsInterface.java | 4 + .../client/graphics/backend/InputHandler.java | 20 +- .../graphics/backend/LWJGLInitializer.java | 19 +- .../client/graphics/gui/BasicButton.java | 13 +- .../client/graphics/gui/Component.java | 129 +----- .../client/graphics/gui/GUILayer.java | 89 ++++- .../client/graphics/gui/RadioButton.java | 12 +- .../client/graphics/gui/menu/MenuLayer.java | 11 +- .../client/graphics/input/InputEvent.java | 36 +- .../client/graphics/input/KeyMatcher.java | 124 ++++-- .../client/graphics/input/Keys.java | 2 +- .../client/graphics/input/bus/Input.java | 62 --- .../client/graphics/input/bus/InputBus.java | 378 ++++++++++++++++-- .../graphics/input/bus/InputListener.java | 2 +- .../client/graphics/world/LayerWorld.java | 12 +- .../windcorp/progressia/test/LayerTestUI.java | 4 +- .../windcorp/progressia/test/TestContent.java | 9 +- .../progressia/test/TestPlayerControls.java | 11 +- 21 files changed, 654 insertions(+), 350 deletions(-) delete mode 100644 src/main/java/ru/windcorp/progressia/client/graphics/input/bus/Input.java diff --git a/src/main/java/ru/windcorp/progressia/client/comms/controls/InputBasedControls.java b/src/main/java/ru/windcorp/progressia/client/comms/controls/InputBasedControls.java index 28b9e1b..1eea281 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/controls/InputBasedControls.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/controls/InputBasedControls.java @@ -19,7 +19,7 @@ package ru.windcorp.progressia.client.comms.controls; import ru.windcorp.progressia.client.Client; -import ru.windcorp.progressia.client.graphics.input.bus.Input; +import ru.windcorp.progressia.client.graphics.input.InputEvent; import ru.windcorp.progressia.common.comms.packets.Packet; public class InputBasedControls { @@ -36,12 +36,12 @@ public class InputBasedControls { .toArray(ControlTriggerInputBased[]::new); } - public void handleInput(Input input) { + public void handleInput(InputEvent event) { for (ControlTriggerInputBased c : controls) { - Packet packet = c.onInputEvent(input.getEvent()); + Packet packet = c.onInputEvent(event); if (packet != null) { - input.consume(); + event.consume(); client.getComms().sendPacket(packet); break; } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/GUI.java b/src/main/java/ru/windcorp/progressia/client/graphics/GUI.java index 59b89da..5834380 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/GUI.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/GUI.java @@ -23,15 +23,8 @@ import java.util.Collections; import java.util.List; import java.util.Objects; -import com.google.common.eventbus.Subscribe; - import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; -import ru.windcorp.progressia.client.graphics.input.CursorEvent; -import ru.windcorp.progressia.client.graphics.input.FrameResizeEvent; import ru.windcorp.progressia.client.graphics.input.InputEvent; -import ru.windcorp.progressia.client.graphics.input.KeyEvent; -import ru.windcorp.progressia.client.graphics.input.WheelEvent; -import ru.windcorp.progressia.client.graphics.input.bus.Input; public class GUI { @@ -46,15 +39,6 @@ public class GUI { private static final List MODIFICATION_QUEUE = Collections .synchronizedList(new ArrayList<>()); - private static class ModifiableInput extends Input { - @Override - public void initialize(InputEvent event, Target target) { - super.initialize(event, target); - } - } - - private static final ModifiableInput THE_INPUT = new ModifiableInput(); - private GUI() { } @@ -126,43 +110,12 @@ public class GUI { LAYERS.forEach(Layer::invalidate); } - private static void dispatchInputEvent(InputEvent event) { - Input.Target target; - - if (event instanceof KeyEvent) { - if (((KeyEvent) event).isMouse()) { - target = Input.Target.HOVERED; - } else { - target = Input.Target.FOCUSED; + public static void dispatchInput(InputEvent event) { + synchronized (LAYERS) { + for (int i = 0; i < LAYERS.size(); ++i) { + LAYERS.get(i).handleInput(event); } - } else if (event instanceof CursorEvent) { - target = Input.Target.HOVERED; - } else if (event instanceof WheelEvent) { - target = Input.Target.HOVERED; - } else if (event instanceof FrameResizeEvent) { - return; - } else { - target = Input.Target.ALL; } - - THE_INPUT.initialize(event, target); - LAYERS.forEach(l -> l.handleInput(THE_INPUT)); - } - - public static Object getEventSubscriber() { - return new Object() { - - @Subscribe - public void onFrameResized(FrameResizeEvent event) { - GUI.invalidateEverything(); - } - - @Subscribe - public void onInput(InputEvent event) { - dispatchInputEvent(event); - } - - }; } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/Layer.java b/src/main/java/ru/windcorp/progressia/client/graphics/Layer.java index dfa72d5..ad685e4 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/Layer.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/Layer.java @@ -21,7 +21,7 @@ package ru.windcorp.progressia.client.graphics; import java.util.concurrent.atomic.AtomicBoolean; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; -import ru.windcorp.progressia.client.graphics.input.bus.Input; +import ru.windcorp.progressia.client.graphics.input.InputEvent; public abstract class Layer { @@ -106,7 +106,7 @@ public abstract class Layer { protected abstract void doRender(); - protected abstract void handleInput(Input input); + public abstract void handleInput(InputEvent input); protected int getWidth() { return GraphicsInterface.getFrameWidth(); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/GraphicsInterface.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/GraphicsInterface.java index ffd0b49..6f59d39 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/GraphicsInterface.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/GraphicsInterface.java @@ -68,6 +68,10 @@ public class GraphicsInterface { public static void subscribeToInputEvents(Object listener) { InputHandler.register(listener); } + + public static void unsubscribeFromInputEvents(Object listener) { + InputHandler.unregister(listener); + } public static void startNextLayer() { GraphicsBackend.startNextLayer(); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/InputHandler.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/InputHandler.java index 34d936c..560ccaf 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/InputHandler.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/InputHandler.java @@ -39,6 +39,7 @@ public class InputHandler { public void initialize(int key, int scancode, int action, int mods) { this.setTime(GraphicsInterface.getTime()); + this.setConsumed(false); this.key = key; this.scancode = scancode; this.action = action; @@ -59,7 +60,7 @@ public class InputHandler { if (GraphicsBackend.getWindowHandle() != window) return; THE_KEY_EVENT.initialize(key, scancode, action, mods); - dispatch(THE_KEY_EVENT); + INPUT_EVENT_BUS.post(THE_KEY_EVENT); switch (action) { case GLFW.GLFW_PRESS: @@ -90,6 +91,7 @@ public class InputHandler { public void initialize(double x, double y) { this.setTime(GraphicsInterface.getTime()); + this.setConsumed(false); getNewPosition().set(x, y); } @@ -109,7 +111,7 @@ public class InputHandler { InputTracker.initializeCursorPosition(x, y); THE_CURSOR_MOVE_EVENT.initialize(x, y); - dispatch(THE_CURSOR_MOVE_EVENT); + INPUT_EVENT_BUS.post(THE_CURSOR_MOVE_EVENT); InputTracker.getCursorPosition().set(x, y); } @@ -124,6 +126,7 @@ public class InputHandler { public void initialize(double xOffset, double yOffset) { this.setTime(GraphicsInterface.getTime()); + this.setConsumed(false); this.getOffset().set(xOffset, yOffset); } @@ -139,7 +142,7 @@ public class InputHandler { if (GraphicsBackend.getWindowHandle() != window) return; THE_WHEEL_SCROLL_EVENT.initialize(xoffset, yoffset); - dispatch(THE_WHEEL_SCROLL_EVENT); + INPUT_EVENT_BUS.post(THE_WHEEL_SCROLL_EVENT); } // FrameResizeEvent @@ -152,6 +155,7 @@ public class InputHandler { public void initialize(int width, int height) { this.setTime(GraphicsInterface.getTime()); + this.setConsumed(false); this.getNewSize().set(width, height); } @@ -167,17 +171,17 @@ public class InputHandler { int height ) { THE_FRAME_RESIZE_EVENT.initialize(width, height); - dispatch(THE_FRAME_RESIZE_EVENT); + INPUT_EVENT_BUS.post(THE_FRAME_RESIZE_EVENT); } // Misc - private static void dispatch(InputEvent event) { - INPUT_EVENT_BUS.post(event); - } - public static void register(Object listener) { INPUT_EVENT_BUS.register(listener); } + + public static void unregister(Object listener) { + INPUT_EVENT_BUS.unregister(listener); + } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/LWJGLInitializer.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/LWJGLInitializer.java index 9239150..9bc280e 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/LWJGLInitializer.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/LWJGLInitializer.java @@ -24,7 +24,11 @@ import static org.lwjgl.system.MemoryUtil.*; import org.lwjgl.opengl.GL; +import com.google.common.eventbus.Subscribe; + import ru.windcorp.progressia.client.graphics.GUI; +import ru.windcorp.progressia.client.graphics.input.FrameResizeEvent; +import ru.windcorp.progressia.client.graphics.input.InputEvent; class LWJGLInitializer { @@ -107,7 +111,20 @@ class LWJGLInitializer { glfwSetScrollCallback(handle, InputHandler::handleWheelScroll); - GraphicsInterface.subscribeToInputEvents(GUI.getEventSubscriber()); + GraphicsInterface.subscribeToInputEvents(new Object() { + + @Subscribe + public void onFrameResized(FrameResizeEvent event) { + GUI.invalidateEverything(); + } + + @Subscribe + public void onInputEvent(InputEvent event) { + GUI.dispatchInput(event); + } + + }); + } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/BasicButton.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/BasicButton.java index 6b86627..20f1ef4 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/BasicButton.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/BasicButton.java @@ -54,18 +54,17 @@ public abstract class BasicButton extends Component { reassembleAt(ARTrigger.HOVER, ARTrigger.FOCUS, ARTrigger.ENABLE); // Click triggers - addListener(KeyEvent.class, e -> { - if (e.isRepeat()) { - return false; - } else if ( + addInputListener(KeyEvent.class, e -> { + if (e.isRepeat()) + return; + + if ( e.isLeftMouseButton() || e.getKey() == GLFW.GLFW_KEY_SPACE || e.getKey() == GLFW.GLFW_KEY_ENTER ) { setPressed(e.isPress()); - return true; - } else { - return false; + e.consume(); } }); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java index 8f1aa9a..417c6ea 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java @@ -25,8 +25,6 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; -import org.lwjgl.glfw.GLFW; - import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; @@ -39,9 +37,10 @@ import ru.windcorp.progressia.client.graphics.gui.event.EnableEvent; import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent; import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent; import ru.windcorp.progressia.client.graphics.gui.event.ParentChangedEvent; +import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent; import ru.windcorp.progressia.client.graphics.input.InputEvent; import ru.windcorp.progressia.client.graphics.input.KeyEvent; -import ru.windcorp.progressia.client.graphics.input.bus.Input; +import ru.windcorp.progressia.client.graphics.input.KeyMatcher; import ru.windcorp.progressia.client.graphics.input.bus.InputBus; import ru.windcorp.progressia.client.graphics.input.bus.InputListener; import ru.windcorp.progressia.common.util.Named; @@ -55,7 +54,7 @@ public class Component extends Named { private Component parent = null; private EventBus eventBus = null; - private InputBus inputBus = null; + private final InputBus inputBus = new InputBus(this); private int x, y; private int width, height; @@ -76,6 +75,9 @@ public class Component extends Named { public Component(String name) { super(name); + + // Update hover flag when cursor moves + addInputListener(CursorMoveEvent.class, this::updateHoverFlag, InputBus.Option.ALWAYS); } public Component getParent() { @@ -521,6 +523,10 @@ public class Component extends Named { dispatchEvent(new HoverEvent(this, isHovered)); } } + + private void updateHoverFlag(CursorMoveEvent e) { + setHovered(contains((int) InputTracker.getCursorX(), (int) InputTracker.getCursorY())); + } public void addListener(Object listener) { if (eventBus == null) { @@ -542,121 +548,28 @@ public class Component extends Named { eventBus.post(event); } - public void addListener( - Class type, - boolean handlesConsumed, - InputListener listener - ) { - if (inputBus == null) { - inputBus = new InputBus(); - } - - inputBus.register(type, handlesConsumed, listener); + public void addInputListener(Class type, InputListener listener, InputBus.Option... options) { + inputBus.register(type, listener, options); + } + + public void addKeyListener(KeyMatcher matcher, InputListener listener, InputBus.Option... options) { + inputBus.register(matcher, listener, options); } - public void addListener(Class type, InputListener listener) { - if (inputBus == null) { - inputBus = new InputBus(); - } - - inputBus.register(type, listener); - } - - public void removeListener(InputListener listener) { + public void removeInputListener(InputListener listener) { if (inputBus != null) { inputBus.unregister(listener); } } - - protected void handleInput(Input input) { - if (inputBus != null && isEnabled()) { - inputBus.dispatch(input); - } + + InputBus getInputBus() { + return inputBus; } - - public void dispatchInput(Input input) { - try { - switch (input.getTarget()) { - case FOCUSED: - dispatchInputToFocused(input); - break; - case HOVERED: - dispatchInputToHovered(input); - break; - case ALL: - default: - dispatchInputToAll(input); - break; - } - } catch (Exception e) { - throw CrashReports.report(e, "Could not dispatch input to Component %s", this); - } - } - - private void dispatchInputToFocused(Input input) { - Component c = findFocused(); - - if (c == null) - return; - if (attemptFocusTransfer(input, c)) - return; - - while (c != null) { - c.handleInput(input); - c = c.getParent(); - } - } - - private void dispatchInputToHovered(Input input) { - getChildren().forEach(child -> { - if (child.containsCursor()) { - child.setHovered(true); - - if (!input.isConsumed()) { - child.dispatchInput(input); - } - } else { - child.setHovered(false); - } - }); - - handleInput(input); - } - - private void dispatchInputToAll(Input input) { - getChildren().forEach(c -> c.dispatchInput(input)); - handleInput(input); - } - - private boolean attemptFocusTransfer(Input input, Component focused) { - if (input.isConsumed()) - return false; - if (!(input.getEvent() instanceof KeyEvent)) - return false; - - KeyEvent keyInput = (KeyEvent) input.getEvent(); - - if (keyInput.getKey() == GLFW.GLFW_KEY_TAB && !keyInput.isRelease()) { - input.consume(); - if (keyInput.hasShift()) { - focused.focusPrevious(); - } else { - focused.focusNext(); - } - return true; - } - - return false; - } - + public synchronized boolean contains(int x, int y) { return x >= getX() && x < getX() + getWidth() && y >= getY() && y < getY() + getHeight(); } - public boolean containsCursor() { - return contains((int) InputTracker.getCursorX(), (int) InputTracker.getCursorY()); - } - public void requestReassembly() { if (parent != null) { parent.requestReassembly(); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/GUILayer.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/GUILayer.java index 2e0981c..b808aed 100755 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/GUILayer.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/GUILayer.java @@ -15,12 +15,19 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + package ru.windcorp.progressia.client.graphics.gui; +import java.util.Iterator; + +import org.lwjgl.glfw.GLFW; + import ru.windcorp.progressia.client.graphics.flat.AssembledFlatLayer; import ru.windcorp.progressia.client.graphics.flat.RenderTarget; -import ru.windcorp.progressia.client.graphics.input.bus.Input; +import ru.windcorp.progressia.client.graphics.input.InputEvent; +import ru.windcorp.progressia.client.graphics.input.KeyEvent; +import ru.windcorp.progressia.client.graphics.input.bus.InputBus; +import ru.windcorp.progressia.common.util.StashingStack; public abstract class GUILayer extends AssembledFlatLayer { @@ -33,7 +40,9 @@ public abstract class GUILayer extends AssembledFlatLayer { public GUILayer(String name, Layout layout) { super(name); + getRoot().setLayout(layout); + getRoot().addInputListener(KeyEvent.class, this::attemptFocusTransfer, InputBus.Option.IGNORE_FOCUS); } public Component getRoot() { @@ -47,9 +56,81 @@ public abstract class GUILayer extends AssembledFlatLayer { getRoot().assemble(target); } + /** + * Stack frame for {@link #handleInput(InputEvent)}. + */ + private static class EventHandlingFrame { + Component component; + Iterator children; + + void init(Component c) { + component = c; + children = c.getChildren().iterator(); + } + + void reset() { + component = null; + children = null; + } + } + + /** + * Stack for {@link #handleInput(InputEvent)}. + */ + private StashingStack path = new StashingStack<>(64, EventHandlingFrame::new); + + /* + * This is essentially a depth-first iteration of the component tree. The + * recursive procedure has been unrolled to reduce call stack length. + */ @Override - protected void handleInput(Input input) { - getRoot().dispatchInput(input); + public void handleInput(InputEvent event) { + if (!path.isEmpty()) { + throw new IllegalStateException( + "path is not empty: " + path + ". Are events being processed concurrently?" + ); + } + + path.push().init(root); + + while (!path.isEmpty()) { + + Iterator it = path.peek().children; + if (it.hasNext()) { + + Component c = it.next(); + + if (c.isEnabled()) { + if (c.getChildren().isEmpty()) { + c.getInputBus().dispatch(event); + } else { + path.push().init(c); + } + } + + } else { + path.peek().component.getInputBus().dispatch(event); + path.pop().reset(); + } + + } + } + + private void attemptFocusTransfer(KeyEvent e) { + Component focused = getRoot().findFocused(); + + if (focused == null) { + return; + } + + if (e.getKey() == GLFW.GLFW_KEY_TAB && !e.isRelease()) { + e.consume(); + if (e.hasShift()) { + focused.focusPrevious(); + } else { + focused.focusNext(); + } + } } @Override diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/RadioButton.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/RadioButton.java index 1ee7f66..8ea76f3 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/RadioButton.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/RadioButton.java @@ -104,26 +104,22 @@ public class RadioButton extends BasicButton { group.addChild(basicChild); addChild(group); - addListener(KeyEvent.class, e -> { - if (e.isRelease()) - return false; + addInputListener(KeyEvent.class, e -> { + if (e.isRelease()) return; if (e.getKey() == GLFW.GLFW_KEY_LEFT || e.getKey() == GLFW.GLFW_KEY_UP) { if (this.group != null) { this.group.selectPrevious(); this.group.getSelected().takeFocus(); } - - return true; + e.consume(); } else if (e.getKey() == GLFW.GLFW_KEY_RIGHT || e.getKey() == GLFW.GLFW_KEY_DOWN) { if (this.group != null) { this.group.selectNext(); this.group.getSelected().takeFocus(); } - return true; + e.consume(); } - - return false; }); addAction(b -> setChecked(true)); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/menu/MenuLayer.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/menu/MenuLayer.java index 4fa155c..2c42cf0 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/menu/MenuLayer.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/menu/MenuLayer.java @@ -33,7 +33,6 @@ import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill; import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical; import ru.windcorp.progressia.client.graphics.input.InputEvent; import ru.windcorp.progressia.client.graphics.input.KeyEvent; -import ru.windcorp.progressia.client.graphics.input.bus.Input; import ru.windcorp.progressia.client.localization.MutableString; import ru.windcorp.progressia.client.localization.MutableStringLocalized; @@ -97,11 +96,9 @@ public class MenuLayer extends GUILayer { } @Override - protected void handleInput(Input input) { + public void handleInput(InputEvent event) { - if (!input.isConsumed()) { - InputEvent event = input.getEvent(); - + if (!event.isConsumed()) { if (event instanceof KeyEvent) { KeyEvent keyEvent = (KeyEvent) event; if (keyEvent.isPress() && keyEvent.getKey() == GLFW.GLFW_KEY_ESCAPE) { @@ -110,8 +107,8 @@ public class MenuLayer extends GUILayer { } } - super.handleInput(input); - input.consume(); + super.handleInput(event); + event.consume(); } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/input/InputEvent.java b/src/main/java/ru/windcorp/progressia/client/graphics/input/InputEvent.java index b0c2483..5edf055 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/input/InputEvent.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/input/InputEvent.java @@ -15,13 +15,35 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + package ru.windcorp.progressia.client.graphics.input; +import ru.windcorp.progressia.client.graphics.gui.Component; + +/** + * An instance of user input. + *

+ * User input events are typically generated by graphics backend between frames + * and passed to the graphics layers from top to bottom. Layers that use + * {@link Component}s will forward this event through the Component hierarchy. + *

+ * Events have a {@code consumed} flag. A freshly-generated event will have this + * flag set to {@code false}. Event listeners that process the event will + * usually choose to raise the flag ("consume the event") to ask future + * listeners to ignore this event. This is done to avoid multiple UI interfaces + * reacting to single input. By default, listeners will not receive consumed + * events; however, some listeners may choose to receive, handle and even + * un-consume the event. + *

+ * {@code InputEvent} objects may be reused for future input events after their + * processing is complete; to obtain a static copy, use {@link #snapshot()}. + */ public abstract class InputEvent { private double time; + private boolean isConsumed = false; + public InputEvent(double time) { this.time = time; } @@ -36,4 +58,16 @@ public abstract class InputEvent { public abstract InputEvent snapshot(); + public boolean isConsumed() { + return isConsumed; + } + + public void setConsumed(boolean isConsumed) { + this.isConsumed = isConsumed; + } + + public void consume() { + setConsumed(true); + } + } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/input/KeyMatcher.java b/src/main/java/ru/windcorp/progressia/client/graphics/input/KeyMatcher.java index 6fedcd6..048ddb5 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/input/KeyMatcher.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/input/KeyMatcher.java @@ -18,16 +18,75 @@ package ru.windcorp.progressia.client.graphics.input; -import java.util.function.Predicate; +import java.util.Map; +import java.util.regex.Pattern; import org.lwjgl.glfw.GLFW; +import com.google.common.collect.ImmutableMap; + public class KeyMatcher { + + private static final Pattern DECLAR_SPLIT_REGEX = Pattern.compile("\\s*\\+\\s*"); + private static final Map MOD_TOKENS = ImmutableMap.of( + "SHIFT", GLFW.GLFW_MOD_SHIFT, + "CONTROL", GLFW.GLFW_MOD_CONTROL, + "ALT", GLFW.GLFW_MOD_ALT, + "SUPER", GLFW.GLFW_MOD_SUPER + ); + + public static final KeyMatcher LMB = new KeyMatcher(GLFW.GLFW_MOUSE_BUTTON_LEFT); + public static final KeyMatcher RMB = new KeyMatcher(GLFW.GLFW_MOUSE_BUTTON_RIGHT); + public static final KeyMatcher MMB = new KeyMatcher(GLFW.GLFW_MOUSE_BUTTON_MIDDLE); private final int key; private final int mods; - protected KeyMatcher(int key, int mods) { + public KeyMatcher(int key, int mods) { + this.key = key; + this.mods = mods; + } + + public KeyMatcher(int key) { + this.key = key; + this.mods = 0; + } + + public KeyMatcher(String declar) { + String[] tokens = DECLAR_SPLIT_REGEX.split(declar); + if (tokens.length == 0) { + throw new IllegalArgumentException("No tokens found in \"" + declar + "\""); + } + + int key = -1; + int mods = 0; + + for (String token : tokens) { + token = token.toUpperCase(); + + if (MOD_TOKENS.containsKey(token)) { + int mod = MOD_TOKENS.get(token); + if ((mods & mod) != 0) { + throw new IllegalArgumentException("Duplicate modifier \"" + token + "\" in \"" + declar + "\""); + } + mods |= mod; + } else if (key != -1) { + throw new IllegalArgumentException("Too many non-modifier tokens in \"" + declar + "\": maximum one key, first offender: \"" + token + "\""); + } else { + token = token.replace(' ', '_'); + + if (token.startsWith("KEYPAD_")) { + token = "KP_" + token.substring("KEYPAD_".length()); + } + + key = Keys.getCode(token); + + if (key == -1) { + throw new IllegalArgumentException("Unknown token \"" + token + "\" in \"" + declar + "\""); + } + } + } + this.key = key; this.mods = mods; } @@ -42,6 +101,15 @@ public class KeyMatcher { return true; } + + public boolean matchesIgnoringAction(KeyEvent event) { + if (event.getKey() != getKey()) + return false; + if ((event.getMods() & getMods()) != getMods()) + return false; + + return true; + } public int getKey() { return key; @@ -50,49 +118,25 @@ public class KeyMatcher { public int getMods() { return mods; } - - public static KeyMatcher.Builder of(int key) { - return new KeyMatcher.Builder(key); + + public KeyMatcher with(int modifier) { + return new KeyMatcher(key, mods | modifier); } - public static class Builder { + public KeyMatcher withShift() { + return with(GLFW.GLFW_MOD_SHIFT); + } - private final int key; - private int mods = 0; + public KeyMatcher withCtrl() { + return with(GLFW.GLFW_MOD_CONTROL); + } - public Builder(int key) { - this.key = key; - } - - public Builder with(int modifier) { - this.mods += modifier; - return this; - } - - public Builder withShift() { - return with(GLFW.GLFW_MOD_SHIFT); - } - - public Builder withCtrl() { - return with(GLFW.GLFW_MOD_CONTROL); - } - - public Builder withAlt() { - return with(GLFW.GLFW_MOD_ALT); - } - - public Builder withSuper() { - return with(GLFW.GLFW_MOD_SUPER); - } - - public KeyMatcher build() { - return new KeyMatcher(key, mods); - } - - public Predicate matcher() { - return build()::matches; - } + public KeyMatcher withAlt() { + return with(GLFW.GLFW_MOD_ALT); + } + public KeyMatcher withSuper() { + return with(GLFW.GLFW_MOD_SUPER); } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/input/Keys.java b/src/main/java/ru/windcorp/progressia/client/graphics/input/Keys.java index 3904f59..9132c37 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/input/Keys.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/input/Keys.java @@ -139,7 +139,7 @@ public class Keys { } public static int getCode(String internalName) { - if (NAMES_TO_CODES.containsKey(internalName)) { + if (!NAMES_TO_CODES.containsKey(internalName)) { return -1; } else { return NAMES_TO_CODES.get(internalName); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/input/bus/Input.java b/src/main/java/ru/windcorp/progressia/client/graphics/input/bus/Input.java deleted file mode 100644 index 1aad6bb..0000000 --- a/src/main/java/ru/windcorp/progressia/client/graphics/input/bus/Input.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Progressia - * Copyright (C) 2020-2021 Wind Corporation and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package ru.windcorp.progressia.client.graphics.input.bus; - -import ru.windcorp.progressia.client.graphics.input.InputEvent; - -public class Input { - - public static enum Target { - FOCUSED, HOVERED, ALL - } - - private InputEvent event; - - private boolean isConsumed; - - private Target target; - - protected void initialize(InputEvent event, Target target) { - this.event = event; - this.target = target; - - this.isConsumed = false; - } - - public InputEvent getEvent() { - return event; - } - - public boolean isConsumed() { - return isConsumed; - } - - public void setConsumed(boolean isConsumed) { - this.isConsumed = isConsumed; - } - - public void consume() { - setConsumed(true); - } - - public Target getTarget() { - return target; - } - -} diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/input/bus/InputBus.java b/src/main/java/ru/windcorp/progressia/client/graphics/input/bus/InputBus.java index a22a243..82f7275 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/input/bus/InputBus.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/input/bus/InputBus.java @@ -15,73 +15,401 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + package ru.windcorp.progressia.client.graphics.input.bus; import java.util.ArrayList; import java.util.Collection; +import java.util.Objects; +import ru.windcorp.jputil.ArrayUtil; +import ru.windcorp.progressia.client.graphics.gui.Component; +import ru.windcorp.progressia.client.graphics.input.CursorEvent; import ru.windcorp.progressia.client.graphics.input.InputEvent; +import ru.windcorp.progressia.client.graphics.input.KeyEvent; +import ru.windcorp.progressia.client.graphics.input.KeyMatcher; +import ru.windcorp.progressia.client.graphics.input.WheelEvent; +import ru.windcorp.progressia.common.util.crash.CrashReports; +/** + * An event bus optionally related to a {@link Component} that delivers input + * events to input listeners. This bus may skip listeners based on circumstance; + * behavior can be customized for each listener with {@link Option}s. + *

+ * By default, events are filtered by four checks before being delivered to each + * listener: + *

    + *
  1. Consumption check: unless {@link Option#RECEIVE_CONSUMED + * RECEIVE_CONSUMED} is set, events that are consumed will not be + * delivered.
  2. + *
  3. Hover check: for certain event types (for example, + * {@link WheelEvent} or {@link KeyEvent} that {@link KeyEvent#isMouse() + * isMouse()}), the event will only be delivered if the component is hovered. + * This check may be bypassed with option {@link Option#IGNORE_HOVER + * IGNORE_HOVER} or made mandatory for all events with + * {@link Option#REQUIRE_HOVER REQUIRE_HOVER}. Hover check automatically + * succeeds if no component is provided.
  4. + *
  5. Focus check: for certain event types (for example, + * {@link KeyEvent} that {@code !isMouse()}), the event will only be delivered + * if the component has focus. This check may be bypassed with option + * {@link Option#IGNORE_FOCUS IGNORE_FOCUS} or made mandatory for all events + * with {@link Option#REQUIRE_FOCUS REQUIRE_FOCUS}. Focus check automatically + * succeeds if no component is provided.
  6. + *
  7. Type check: events of type {@code E} are only delivered to + * listeners registered with event type {@code T} if objects of type {@code E} + * can be cast to {@code T}.
  8. + *
+ * Checks 1-3 are bypassed when option {@link Option#ALWAYS ALWAYS} is + * specified. + */ public class InputBus { - private static class WrappedListener { + /** + * Options that allow customization of checks for listeners. + */ + public enum Option { + + /** + * Ignore checks for consumed events, hover and focus; deliver event if + * at all possible. This is shorthand for {@link #RECEIVE_CONSUMED}, + * {@link #IGNORE_HOVER} and {@link #IGNORE_FOCUS}. + */ + ALWAYS, + + /** + * Receive events that were previously consumed. + */ + RECEIVE_CONSUMED, + + /** + * Do not process events if the listener is registered with a component + * and the component is not hovered. + */ + REQUIRE_HOVER, + + /** + * Deliver events even if the event is limited to hovered components by + * default. + */ + IGNORE_HOVER, + + /** + * Do not process events if the listener is registered with a component + * and the component is not focused. + */ + REQUIRE_FOCUS, + + /** + * Deliver events even if the event is limited to focused components by + * default. + */ + IGNORE_FOCUS, + + /** + * Deliver events according to + * {@link KeyMatcher#matchesIgnoringAction(KeyEvent)} rather than + * {@link KeyMatcher#matches(KeyEvent)} when a {@link KeyMatcher} is + * specified. + */ + IGNORE_ACTION; + + } + + private enum YesNoDefault { + YES, NO, DEFAULT; + } + + /** + * A listener with check preferences resolved and type specified. + */ + private class WrappedListener { private final Class type; - private final boolean handleConsumed; + + private final boolean dropIfConsumed; + private final YesNoDefault dropIfNotHovered; + private final YesNoDefault dropIfNotFocused; + private final InputListener listener; public WrappedListener( Class type, - boolean handleConsumed, + boolean dropIfConsumed, + YesNoDefault dropIfNotHovered, + YesNoDefault dropIfNotFocused, InputListener listener ) { this.type = type; - this.handleConsumed = handleConsumed; + this.dropIfConsumed = dropIfConsumed; + this.dropIfNotHovered = dropIfNotHovered; + this.dropIfNotFocused = dropIfNotFocused; this.listener = listener; } - private boolean handles(Input input) { - return (!input.isConsumed() || handleConsumed) && - type.isInstance(input.getEvent()); + private boolean handles(InputEvent input) { + if (dropIfConsumed && input.isConsumed()) + return false; + + switch (dropIfNotHovered) { + case YES: + if (!isHovered()) + return false; + break; + case NO: + break; + default: + + if (isHovered()) + break; + + if (input instanceof KeyEvent && ((KeyEvent) input).isMouse()) + return false; + + if (input instanceof CursorEvent) + return false; + + if (input instanceof WheelEvent) + return false; + + break; + } + + switch (dropIfNotFocused) { + case YES: + if (!isFocused()) + return false; + break; + case NO: + break; + default: + + if (isFocused()) + break; + + if (input instanceof KeyEvent && !((KeyEvent) input).isMouse()) + return false; + + break; + } + + if (!type.isInstance(input)) + return false; + + return true; } + /** + * Invokes the listener if the event is deemed appropriate by the four + * checks. + * + * @param event the event to deliver + */ @SuppressWarnings("unchecked") - public void handle(Input input) { - if (handles(input)) { - boolean consumed = ((InputListener) listener) - .handle( - (InputEvent) type.cast(input.getEvent()) - ); + public void handle(InputEvent event) { + if (handles(event)) { + // A runtime check of types has been performed; this is safe. + InputListener castListener = (InputListener) listener; - input.setConsumed(consumed); + try { + castListener.handle(event); + } catch (Exception e) { + throw CrashReports.report( + e, + "InputListener %s for component %s has failed to receive event %s", + listener, + owner, + event + ); + } } } } + /** + * The component queried for focus and hover. May be {@code null}. + */ + private final Component owner; + + /** + * Registered listeners. + */ private final Collection listeners = new ArrayList<>(4); - public void dispatch(Input input) { - listeners.forEach(l -> l.handle(input)); + /** + * Creates a new input bus that consults the specified {@link Component} to + * determine hover and focus. + * + * @param owner the component to use for hover and focus tests + * @see #InputBus() + */ + public InputBus(Component owner) { + this.owner = Objects.requireNonNull(owner, "owner"); } + /** + * Creates a new input bus that assumes all hover and focus checks are + * successful. + * + * @see #InputBus(Component) + */ + public InputBus() { + this.owner = null; + } + + /** + * Determines whether hover should be assumed for this event bus. + * + * @return {@code true} iff no component is linked or the linked component + * is hovered + */ + private boolean isHovered() { + return owner == null ? true : owner.isHovered(); + } + + /** + * Determines whether focus should be assumed for this event bus. + * + * @return {@code true} iff no component is linked or the linked component + * is focused + */ + private boolean isFocused() { + return owner == null ? true : owner.isFocused(); + } + + /** + * Dispatches (delivers) the provided event to all appropriate listeners. + * + * @param event the event to process + */ + public void dispatch(InputEvent event) { + Objects.requireNonNull(event, "event"); + for (WrappedListener listener : listeners) { + listener.handle(event); + } + } + + /** + * Registers a listener on this bus. + *

+ * {@code type} specifies the class of events that should be passed to this + * listener. Only events of types that extend, implement or equal + * {@code type} are processed. + *

+ * Zero or more {@link Option}s may be specified to enable or disable the + * processing of certain events in certain circumstances. See + * {@linkplain InputBus class description} for a detailed breakdown of the + * checks performed and the effects of various options. When providing + * options to this method, later options override the effects of previous + * options. + *

+ * Option {@link Option#IGNORE_ACTION IGNORE_ACTION} is ignored silently. + * + * @param type the event class to deliver + * @param listener the listener + * @param options the options for this listener + */ public void register( Class type, - boolean handlesConsumed, - InputListener listener + InputListener listener, + Option... options ) { - listeners.add(new WrappedListener(type, handlesConsumed, listener)); + Objects.requireNonNull(type, "type"); + Objects.requireNonNull(listener, "listener"); + + boolean dropIfConsumed = true; + YesNoDefault dropIfNotHovered = YesNoDefault.DEFAULT; + YesNoDefault dropIfNotFocused = YesNoDefault.DEFAULT; + + if (options != null) { + for (Option option : options) { + switch (option) { + case ALWAYS: + dropIfConsumed = false; + dropIfNotHovered = YesNoDefault.NO; + dropIfNotFocused = YesNoDefault.NO; + break; + case RECEIVE_CONSUMED: + dropIfConsumed = false; + break; + case REQUIRE_HOVER: + dropIfNotHovered = YesNoDefault.YES; + break; + case IGNORE_HOVER: + dropIfNotFocused = YesNoDefault.NO; + break; + case REQUIRE_FOCUS: + dropIfNotHovered = YesNoDefault.YES; + break; + case IGNORE_FOCUS: + dropIfNotFocused = YesNoDefault.NO; + break; + case IGNORE_ACTION: + // Ignore + break; + default: + throw new IllegalArgumentException("Unexpected option " + option); + } + } + } + + listeners.add(new WrappedListener(type, dropIfConsumed, dropIfNotHovered, dropIfNotFocused, listener)); } - public void register( - Class type, - InputListener listener - ) { - register(type, false, listener); + /** + * Registers a {@link KeyEvent} listener on this bus. An event has to match + * the provided {@link KeyMatcher} to be delivered to the listener. + *

+ * Zero or more {@link Option}s may be specified to enable or disable the + * processing of certain events in certain circumstances. See + * {@linkplain InputBus class description} for a detailed breakdown of the + * checks performed and the effects of various options. When providing + * options to this method, later options override the effects of previous + * options. + *

+ * Option {@link Option#IGNORE_ACTION IGNORE_ACTION} requests that events + * are delivered according to + * {@link KeyMatcher#matchesIgnoringAction(KeyEvent)} rather than + * {@link KeyMatcher#matches(KeyEvent)}. + * specified. + * + * @param matcher an event filter + * @param listener the listener + * @param options the options for this listener + */ + public void register(KeyMatcher matcher, InputListener listener, Option... options) { + Objects.requireNonNull(matcher, "matcher"); + Objects.requireNonNull(listener, "listener"); + + InputListener filteringListener; + + if (ArrayUtil.firstIndexOf(options, Option.IGNORE_ACTION) != -1) { + filteringListener = e -> { + if (matcher.matchesIgnoringAction(e)) { + listener.handle(e); + } + }; + } else { + filteringListener = e -> { + if (matcher.matches(e)) { + listener.handle(e); + } + }; + } + + register(KeyEvent.class, filteringListener, options); } + /** + * Removes all occurrences of the provided listener from this bus. + * + * @param listener the listener to unregister + */ public void unregister(InputListener listener) { + if (listener == null) { + return; + } + listeners.removeIf(l -> l.listener == listener); } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/input/bus/InputListener.java b/src/main/java/ru/windcorp/progressia/client/graphics/input/bus/InputListener.java index 0d68b5e..1db2410 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/input/bus/InputListener.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/input/bus/InputListener.java @@ -23,6 +23,6 @@ import ru.windcorp.progressia.client.graphics.input.InputEvent; @FunctionalInterface public interface InputListener { - boolean handle(T event); + void handle(T event); } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java index c81e7a7..b25eaa5 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java @@ -31,7 +31,7 @@ import ru.windcorp.progressia.client.comms.controls.InputBasedControls; import ru.windcorp.progressia.client.graphics.Layer; import ru.windcorp.progressia.client.graphics.backend.FaceCulling; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; -import ru.windcorp.progressia.client.graphics.input.bus.Input; +import ru.windcorp.progressia.client.graphics.input.InputEvent; import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram; import ru.windcorp.progressia.client.graphics.model.Shapes.PppBuilder; @@ -225,14 +225,14 @@ public class LayerWorld extends Layer { } @Override - protected void handleInput(Input input) { - if (input.isConsumed()) + public void handleInput(InputEvent event) { + if (event.isConsumed()) return; - tmp_testControls.handleInput(input); + tmp_testControls.handleInput(event); - if (!input.isConsumed()) { - inputBasedControls.handleInput(input); + if (!event.isConsumed()) { + inputBasedControls.handleInput(event); } } diff --git a/src/main/java/ru/windcorp/progressia/test/LayerTestUI.java b/src/main/java/ru/windcorp/progressia/test/LayerTestUI.java index 19c10aa..686fc3a 100755 --- a/src/main/java/ru/windcorp/progressia/test/LayerTestUI.java +++ b/src/main/java/ru/windcorp/progressia/test/LayerTestUI.java @@ -27,8 +27,8 @@ import ru.windcorp.progressia.client.graphics.Colors; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; import ru.windcorp.progressia.client.graphics.flat.AssembledFlatLayer; import ru.windcorp.progressia.client.graphics.flat.RenderTarget; +import ru.windcorp.progressia.client.graphics.input.InputEvent; import ru.windcorp.progressia.client.graphics.input.KeyEvent; -import ru.windcorp.progressia.client.graphics.input.bus.Input; public class LayerTestUI extends AssembledFlatLayer { @@ -91,7 +91,7 @@ public class LayerTestUI extends AssembledFlatLayer { } @Override - protected void handleInput(Input input) { + public void handleInput(InputEvent event) { // Do nothing } diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index c2444d2..5f136aa 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -28,7 +28,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Consumer; -import org.lwjgl.glfw.GLFW; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.client.ClientState; @@ -298,7 +297,7 @@ public class TestContent { "Test:BreakBlock", KeyEvent.class, TestContent::onBlockBreakTrigger, - KeyMatcher.of(GLFW.GLFW_MOUSE_BUTTON_LEFT).matcher(), + KeyMatcher.LMB::matches, i -> isAnythingSelected() ) ); @@ -310,7 +309,7 @@ public class TestContent { "Test:PlaceBlock", KeyEvent.class, TestContent::onBlockPlaceTrigger, - KeyMatcher.of(GLFW.GLFW_MOUSE_BUTTON_RIGHT).matcher(), + KeyMatcher.RMB::matches, i -> isAnythingSelected() && TestPlayerControls.getInstance().isBlockSelected() ) ); @@ -323,7 +322,7 @@ public class TestContent { "Test:PlaceTile", KeyEvent.class, TestContent::onTilePlaceTrigger, - KeyMatcher.of(GLFW.GLFW_MOUSE_BUTTON_RIGHT).matcher(), + KeyMatcher.RMB::matches, i -> isAnythingSelected() && !TestPlayerControls.getInstance().isBlockSelected() ) ); @@ -334,7 +333,7 @@ public class TestContent { "Test:StartNextMusic", KeyEvent.class, TestMusicPlayer::startNextNow, - KeyMatcher.of(GLFW.GLFW_KEY_M).matcher() + new KeyMatcher("M")::matches ) ); } diff --git a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java index 4ca0ea6..8c03c99 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java +++ b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java @@ -32,7 +32,6 @@ import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent; import ru.windcorp.progressia.client.graphics.input.InputEvent; import ru.windcorp.progressia.client.graphics.input.KeyEvent; import ru.windcorp.progressia.client.graphics.input.WheelScrollEvent; -import ru.windcorp.progressia.client.graphics.input.bus.Input; import ru.windcorp.progressia.client.graphics.world.LocalPlayer; import ru.windcorp.progressia.client.localization.Localizer; import ru.windcorp.progressia.common.Units; @@ -162,19 +161,17 @@ public class TestPlayerControls { ); } - public void handleInput(Input input) { - InputEvent event = input.getEvent(); - + public void handleInput(InputEvent event) { if (event instanceof KeyEvent) { if (onKeyEvent((KeyEvent) event)) { - input.consume(); + event.consume(); } } else if (event instanceof CursorMoveEvent) { onMouseMoved((CursorMoveEvent) event); - input.consume(); + event.consume(); } else if (event instanceof WheelScrollEvent) { onWheelScroll((WheelScrollEvent) event); - input.consume(); + event.consume(); } }