diff --git a/spring-shell-core/src/main/java/org/springframework/shell/component/view/TerminalUI.java b/spring-shell-core/src/main/java/org/springframework/shell/component/view/TerminalUI.java index 9d61c9a7..eb98f59d 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/component/view/TerminalUI.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/component/view/TerminalUI.java @@ -33,8 +33,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; import org.springframework.shell.component.view.control.View; import org.springframework.shell.component.view.control.ViewService; import org.springframework.shell.component.view.event.DefaultEventLoop; @@ -48,10 +46,8 @@ import org.springframework.shell.component.view.event.MouseHandler; import org.springframework.shell.component.view.event.MouseHandler.MouseHandlerResult; import org.springframework.shell.component.view.geom.Rectangle; import org.springframework.shell.component.view.message.ShellMessageBuilder; -import org.springframework.shell.component.view.message.ShellMessageHeaderAccessor; import org.springframework.shell.component.view.screen.DefaultScreen; import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -236,40 +232,24 @@ public class TerminalUI implements ViewService { } private void dispatchWinch() { - Message message = MessageBuilder.withPayload("WINCH") - .setHeader(ShellMessageHeaderAccessor.EVENT_TYPE, EventLoop.Type.SIGNAL) - .build(); - eventLoop.dispatch(message); + eventLoop.dispatch(ShellMessageBuilder.ofSignal("WINCH")); } private void registerEventHandling() { - // XXX: think this again - eventLoop.onDestroy(eventLoop.events() - .filter(m -> { - return ObjectUtils.nullSafeEquals(m.getHeaders().get(ShellMessageHeaderAccessor.EVENT_TYPE), EventLoop.Type.SIGNAL); - }) - .doOnNext(m -> { + eventLoop.onDestroy(eventLoop.signalEvents() + .subscribe(event -> { display(); - }) - .subscribe()); + })); - // XXX: think this again - eventLoop.onDestroy(eventLoop.events() - .filter(m -> { - return ObjectUtils.nullSafeEquals(m.getHeaders().get(ShellMessageHeaderAccessor.EVENT_TYPE), EventLoop.Type.SYSTEM); - }) - .doOnNext(m -> { - Object payload = m.getPayload(); - if (payload instanceof String s) { - if ("redraw".equals(s)) { - display(); - } - else if ("int".equals(s)) { - this.terminal.raise(Signal.INT); - } + eventLoop.onDestroy(eventLoop.systemEvents() + .subscribe(event -> { + if ("redraw".equals(event)) { + display(); } - }) - .subscribe()); + else if ("int".equals(event)) { + this.terminal.raise(Signal.INT); + } + })); eventLoop.onDestroy(eventLoop.keyEvents() .doOnNext(m -> { @@ -417,11 +397,8 @@ public class TerminalUI implements ViewService { } private void dispatchKeyEvent(KeyEvent event) { - Message message = MessageBuilder - .withPayload(event) - .setHeader(ShellMessageHeaderAccessor.EVENT_TYPE, EventLoop.Type.KEY) - .build(); - eventLoop.dispatch(message); + log.debug("Dispatch key event: {}", event); + eventLoop.dispatch(ShellMessageBuilder.ofKeyEvent(event)); } private void dispatchMouse(MouseEvent event) { diff --git a/spring-shell-core/src/main/java/org/springframework/shell/component/view/event/DefaultEventLoop.java b/spring-shell-core/src/main/java/org/springframework/shell/component/view/event/DefaultEventLoop.java index cf9bc69b..4e4fa9a0 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/component/view/event/DefaultEventLoop.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/component/view/event/DefaultEventLoop.java @@ -150,6 +150,22 @@ public class DefaultEventLoop implements EventLoop { .ofType(MouseEvent.class); } + @Override + public Flux systemEvents() { + return events() + .filter(m -> EventLoop.Type.SYSTEM.equals(StaticShellMessageHeaderAccessor.getEventType(m))) + .map(m -> m.getPayload()) + .ofType(String.class); + } + + @Override + public Flux signalEvents() { + return events() + .filter(m -> EventLoop.Type.SIGNAL.equals(StaticShellMessageHeaderAccessor.getEventType(m))) + .map(m -> m.getPayload()) + .ofType(String.class); + } + @Override public Flux viewEvents(Class clazz) { return events(EventLoop.Type.VIEW, clazz); diff --git a/spring-shell-core/src/main/java/org/springframework/shell/component/view/event/EventLoop.java b/spring-shell-core/src/main/java/org/springframework/shell/component/view/event/EventLoop.java index dc6d7fa4..9a4cd53f 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/component/view/event/EventLoop.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/component/view/event/EventLoop.java @@ -61,6 +61,22 @@ public interface EventLoop { */ Flux mouseEvents(); + /** + * Specialisation of {@link #events()} which returns type safe + * {code system} events. + * + * @return the system events from an event loop + */ + Flux systemEvents(); + + /** + * Specialisation of {@link #events()} which returns type safe + * {code signal} events. + * + * @return the signal events from an event loop + */ + Flux signalEvents(); + /** * Specialisation of {@link #events()} which returns type safe {@link ViewEvent}s. * diff --git a/spring-shell-core/src/main/java/org/springframework/shell/component/view/message/ShellMessageBuilder.java b/spring-shell-core/src/main/java/org/springframework/shell/component/view/message/ShellMessageBuilder.java index 2ebc25d0..180b1741 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/component/view/message/ShellMessageBuilder.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/component/view/message/ShellMessageBuilder.java @@ -20,6 +20,7 @@ import org.springframework.messaging.Message; import org.springframework.messaging.support.GenericMessage; import org.springframework.shell.component.view.control.View; import org.springframework.shell.component.view.event.EventLoop; +import org.springframework.shell.component.view.event.KeyEvent; import org.springframework.shell.component.view.event.MouseEvent; import org.springframework.util.Assert; @@ -83,6 +84,30 @@ public final class ShellMessageBuilder { .build(); } + /** + * Create a {@code signal} message. + * + * @return a signal message + */ + public static Message ofSignal(String signal) { + return new ShellMessageBuilder<>(signal, null) + .setEventType(EventLoop.Type.SIGNAL) + .setPriority(0) + .build(); + } + + /** + * Create a message of a {@link KeyEvent}. + * + * @param event the event type + * @return a message with {@link KeyEvent} as a payload + */ + public static Message ofKeyEvent(KeyEvent event) { + return new ShellMessageBuilder<>(event, null) + .setEventType(EventLoop.Type.KEY) + .build(); + } + /** * Create a message of a {@link MouseEvent}. * diff --git a/spring-shell-core/src/test/java/org/springframework/shell/component/view/event/DefaultEventLoopTests.java b/spring-shell-core/src/test/java/org/springframework/shell/component/view/event/DefaultEventLoopTests.java index 66ea2ac6..b35a1cf0 100644 --- a/spring-shell-core/src/test/java/org/springframework/shell/component/view/event/DefaultEventLoopTests.java +++ b/spring-shell-core/src/test/java/org/springframework/shell/component/view/event/DefaultEventLoopTests.java @@ -17,6 +17,7 @@ package org.springframework.shell.component.view.event; import java.time.Duration; import java.util.Arrays; +import java.util.EnumSet; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -190,4 +191,70 @@ class DefaultEventLoopTests { } } + @Test + void keyEvents() { + initDefault(); + + KeyEvent event = KeyEvent.of(KeyEvent.Key.a); + Message message = ShellMessageBuilder.ofKeyEvent(event); + + StepVerifier verifier1 = StepVerifier.create(loop.keyEvents()) + .expectNextCount(1) + .thenCancel() + .verifyLater(); + + loop.dispatch(message); + verifier1.verify(Duration.ofSeconds(1)); + } + + @Test + void mouseEvents() { + initDefault(); + + org.jline.terminal.MouseEvent jlineMouseEvent = new org.jline.terminal.MouseEvent( + org.jline.terminal.MouseEvent.Type.Released, + org.jline.terminal.MouseEvent.Button.Button1, + EnumSet.noneOf(org.jline.terminal.MouseEvent.Modifier.class), 0, 0); + MouseEvent event = MouseEvent.of(jlineMouseEvent); + Message message = ShellMessageBuilder.ofMouseEvent(event); + + StepVerifier verifier1 = StepVerifier.create(loop.mouseEvents()) + .expectNextCount(1) + .thenCancel() + .verifyLater(); + + loop.dispatch(message); + verifier1.verify(Duration.ofSeconds(1)); + } + + @Test + void systemEvents() { + initDefault(); + + Message message = ShellMessageBuilder.ofRedraw(); + + StepVerifier verifier1 = StepVerifier.create(loop.systemEvents()) + .expectNextCount(1) + .thenCancel() + .verifyLater(); + + loop.dispatch(message); + verifier1.verify(Duration.ofSeconds(1)); + } + + @Test + void signalEvents() { + initDefault(); + + Message message = ShellMessageBuilder.ofSignal("WINCH"); + + StepVerifier verifier1 = StepVerifier.create(loop.signalEvents()) + .expectNextCount(1) + .thenCancel() + .verifyLater(); + + loop.dispatch(message); + verifier1.verify(Duration.ofSeconds(1)); + } + }