Support view commands in view
- Setup facilities in view system to register commands with a Runnable to get dispatched into an event loop. - As first commands, setup ListView with lineDown and lineUp. - Expose needed functions into View itself. - Relates #867
This commit is contained in:
@@ -17,6 +17,7 @@ package org.springframework.shell.component.view.control;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@@ -55,6 +56,7 @@ public abstract class AbstractView extends AbstractControl implements View {
|
||||
private int layer;
|
||||
private EventLoop eventLoop;
|
||||
private ViewService viewService;
|
||||
private final Map<String, Runnable> commands = new HashMap<>();
|
||||
private Map<Integer, KeyBindingValue> keyBindings = new HashMap<>();
|
||||
private Map<Integer, KeyBindingValue> hotKeyBindings = new HashMap<>();
|
||||
private Map<Integer, MouseBindingValue> mouseBindings = new HashMap<>();
|
||||
@@ -173,7 +175,7 @@ public abstract class AbstractView extends AbstractControl implements View {
|
||||
if (key != null) {
|
||||
KeyBindingValue keyBindingValue = getKeyBindings().get(key);
|
||||
if (keyBindingValue != null) {
|
||||
consumed = dispatchRunCommand(event, keyBindingValue);
|
||||
consumed = dispatchKeyRunCommand(event, keyBindingValue);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -192,7 +194,7 @@ public abstract class AbstractView extends AbstractControl implements View {
|
||||
if (key != null) {
|
||||
KeyBindingValue keyBindingValue = getHotKeyBindings().get(key);
|
||||
if (keyBindingValue != null) {
|
||||
consumed = dispatchRunCommand(event, keyBindingValue);
|
||||
consumed = dispatchKeyRunCommand(event, keyBindingValue);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -257,6 +259,15 @@ public abstract class AbstractView extends AbstractControl implements View {
|
||||
return viewService;
|
||||
}
|
||||
|
||||
protected void registerViewCommand(String command, Runnable runnable) {
|
||||
commands.put(command, runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getViewCommands() {
|
||||
return commands.keySet();
|
||||
}
|
||||
|
||||
protected void registerKeyBinding(Integer keyType, String keyCommand) {
|
||||
registerKeyBinding(keyType, keyCommand, null, null);
|
||||
}
|
||||
@@ -395,20 +406,43 @@ public abstract class AbstractView extends AbstractControl implements View {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean dispatchRunCommand(KeyEvent event, KeyBindingValue command) {
|
||||
@Override
|
||||
public boolean runViewCommand(String command) {
|
||||
if (eventLoop == null) {
|
||||
return false;
|
||||
}
|
||||
Runnable runnable = command.keyRunnable();
|
||||
if (runnable != null) {
|
||||
if (command != null) {
|
||||
Runnable runnable = commands.get(command);
|
||||
if (runnable != null) {
|
||||
Message<Runnable> message = ShellMessageBuilder
|
||||
.withPayload(runnable)
|
||||
.setEventType(EventLoop.Type.TASK)
|
||||
.build();
|
||||
dispatch(message);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean dispatchKeyRunCommand(KeyEvent event, KeyBindingValue keyBindingValue) {
|
||||
if (eventLoop == null) {
|
||||
return false;
|
||||
}
|
||||
String keyCommand = keyBindingValue.keyCommand();
|
||||
if (runViewCommand(keyCommand)) {
|
||||
return true;
|
||||
}
|
||||
Runnable keyRunnable = keyBindingValue.keyRunnable();
|
||||
if (keyRunnable != null) {
|
||||
Message<Runnable> message = ShellMessageBuilder
|
||||
.withPayload(runnable)
|
||||
.withPayload(keyRunnable)
|
||||
.setEventType(EventLoop.Type.TASK)
|
||||
.build();
|
||||
dispatch(message);
|
||||
return true;
|
||||
}
|
||||
KeyBindingConsumer keyConsumer = command.keyConsumer();
|
||||
KeyBindingConsumer keyConsumer = keyBindingValue.keyConsumer();
|
||||
if (keyConsumer != null) {
|
||||
Message<KeyBindingConsumerArgs> message = ShellMessageBuilder
|
||||
.withPayload(new KeyBindingConsumerArgs(keyConsumer, event))
|
||||
@@ -420,20 +454,24 @@ public abstract class AbstractView extends AbstractControl implements View {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean dispatchMouseRunCommand(MouseEvent event, MouseBindingValue command) {
|
||||
protected boolean dispatchMouseRunCommand(MouseEvent event, MouseBindingValue mouseBindingValue) {
|
||||
if (eventLoop == null) {
|
||||
return false;
|
||||
}
|
||||
Runnable runnable = command.mouseRunnable();
|
||||
if (runnable != null) {
|
||||
String mouseCommand = mouseBindingValue.mouseCommand();
|
||||
if (runViewCommand(mouseCommand)) {
|
||||
return true;
|
||||
}
|
||||
Runnable mouseRunnable = mouseBindingValue.mouseRunnable();
|
||||
if (mouseRunnable != null) {
|
||||
Message<Runnable> message = ShellMessageBuilder
|
||||
.withPayload(runnable)
|
||||
.withPayload(mouseRunnable)
|
||||
.setEventType(EventLoop.Type.TASK)
|
||||
.build();
|
||||
dispatch(message);
|
||||
return true;
|
||||
}
|
||||
MouseBindingConsumer mouseConsumer = command.mouseConsumer();
|
||||
MouseBindingConsumer mouseConsumer = mouseBindingValue.mouseConsumer();
|
||||
if (mouseConsumer != null) {
|
||||
Message<MouseBindingConsumerArgs> message = ShellMessageBuilder
|
||||
.withPayload(new MouseBindingConsumerArgs(mouseConsumer, event))
|
||||
|
||||
@@ -38,6 +38,12 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* {@code ListView} is a {@link View} showing items in a vertical list.
|
||||
*
|
||||
* <p>Supported view commands:
|
||||
* <ul>
|
||||
* <li>{@link ViewCommand#LINE_UP} - Move active line upwards.
|
||||
* <li>{@link ViewCommand#LINE_DOWN} - Move active line downwards.
|
||||
* </ul>
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public class ListView<T> extends BoxView {
|
||||
@@ -94,13 +100,16 @@ public class ListView<T> extends BoxView {
|
||||
|
||||
@Override
|
||||
protected void initInternal() {
|
||||
registerKeyBinding(Key.CursorUp, () -> up());
|
||||
registerKeyBinding(Key.CursorDown, () -> down());
|
||||
registerViewCommand(ViewCommand.LINE_UP, () -> up());
|
||||
registerViewCommand(ViewCommand.LINE_DOWN, () -> down());
|
||||
|
||||
registerKeyBinding(Key.CursorUp, ViewCommand.LINE_UP);
|
||||
registerKeyBinding(Key.CursorDown, ViewCommand.LINE_DOWN);
|
||||
registerKeyBinding(Key.Enter, () -> enter());
|
||||
registerKeyBinding(Key.Space, () -> space());
|
||||
|
||||
registerMouseBinding(MouseEvent.Type.Wheel | MouseEvent.Button.WheelUp, () -> up());
|
||||
registerMouseBinding(MouseEvent.Type.Wheel | MouseEvent.Button.WheelDown, () -> down());
|
||||
registerMouseBinding(MouseEvent.Type.Wheel | MouseEvent.Button.WheelUp, ViewCommand.LINE_UP);
|
||||
registerMouseBinding(MouseEvent.Type.Wheel | MouseEvent.Button.WheelDown, ViewCommand.LINE_DOWN);
|
||||
registerMouseBinding(MouseEvent.Type.Released | MouseEvent.Button.Button1, event -> click(event));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.shell.component.view.control;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.shell.component.view.event.EventLoop;
|
||||
import org.springframework.shell.component.view.event.KeyHandler;
|
||||
@@ -87,4 +89,20 @@ public interface View extends Control {
|
||||
*/
|
||||
void setEventLoop(@Nullable EventLoop eventLoop);
|
||||
|
||||
/**
|
||||
* Get supported commands.
|
||||
*
|
||||
* @return supported commands
|
||||
* @see ViewCommand
|
||||
*/
|
||||
Set<String> getViewCommands();
|
||||
|
||||
/**
|
||||
* Run command.
|
||||
*
|
||||
* @param command the command to run
|
||||
* @return true if command was succesfully dispatched
|
||||
* @see ViewCommand
|
||||
*/
|
||||
boolean runViewCommand(String command);
|
||||
}
|
||||
|
||||
@@ -26,25 +26,17 @@ package org.springframework.shell.component.view.control;
|
||||
public final class ViewCommand {
|
||||
|
||||
/**
|
||||
* Move line up. Generic use in something where selection needs to be moved up.
|
||||
* Move line up.
|
||||
*
|
||||
* For example where selection needs to be moved up.
|
||||
*/
|
||||
public static String LINE_UP = "LineUp";
|
||||
|
||||
/**
|
||||
* Move line down. Generic use in something where selection needs to be moved
|
||||
* down.
|
||||
* Move line down.
|
||||
*
|
||||
* For example where selection needs to be moved down.
|
||||
*/
|
||||
public static String LINE_DOWN = "LineDown";
|
||||
|
||||
/**
|
||||
* Open selected item. In a some sort of view where something can be selected
|
||||
* and that active selected should be opened.
|
||||
*/
|
||||
public static String OPEN_SELECTED_ITEM = "OpenSelectedItem";
|
||||
|
||||
public static String SELECT = "Select";
|
||||
public static String LEFT = "Left";
|
||||
public static String RIGHT = "Right";
|
||||
public static String SELECTION_CHANGED = "SelectionChanged";
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.shell.component.view.control;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -474,4 +475,46 @@ class ListViewTests extends AbstractViewTests {
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ViewCommands {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
view = new ListView<>();
|
||||
configure(view);
|
||||
view.setRect(0, 0, 80, 24);
|
||||
view.setItems(Arrays.asList("item1", "item2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void supports() {
|
||||
assertThat(view.getViewCommands()).contains(ViewCommand.LINE_DOWN, ViewCommand.LINE_UP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void lineDown() {
|
||||
StepVerifier verifier = StepVerifier.create(eventLoop.events())
|
||||
.expectNextCount(1)
|
||||
.thenCancel()
|
||||
.verifyLater();
|
||||
view.runViewCommand(ViewCommand.LINE_DOWN);
|
||||
verifier.verify(Duration.ofSeconds(1));
|
||||
assertThat(getIntField(view, START_FIELD)).isEqualTo(0);
|
||||
assertThat(getIntField(view, POSITION_FIELD)).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void lineUp() {
|
||||
StepVerifier verifier = StepVerifier.create(eventLoop.events())
|
||||
.expectNextCount(1)
|
||||
.thenCancel()
|
||||
.verifyLater();
|
||||
view.runViewCommand(ViewCommand.LINE_UP);
|
||||
verifier.verify(Duration.ofSeconds(1));
|
||||
assertThat(getIntField(view, START_FIELD)).isEqualTo(0);
|
||||
assertThat(getIntField(view, POSITION_FIELD)).isEqualTo(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user