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 60b8b51aa..a4a05b063 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 @@ -190,6 +190,7 @@ public ViewService getViewService() { * @param view the view to configure */ public void configure(View view) { + view.init(); view.setEventLoop(eventLoop); view.setThemeResolver(themeResolver); view.setThemeName(themeName); diff --git a/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/AbstractView.java b/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/AbstractView.java index 0e1042fb2..f05a34827 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/AbstractView.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/AbstractView.java @@ -60,10 +60,7 @@ public abstract class AbstractView extends AbstractControl implements View { private Map keyBindings = new HashMap<>(); private Map hotKeyBindings = new HashMap<>(); private Map mouseBindings = new HashMap<>(); - - public AbstractView() { - init(); - } + private boolean init = false; /** * Register {@link Disposable} to get disposed when view terminates. @@ -88,8 +85,20 @@ public void destroy() { * * @see #initInternal() */ - protected final void init() { + @Override + public final void init() { + if (init) { + return; + } initInternal(); + init = true; + } + + private Integer shortcutKey; + private Runnable shortcutAction; + public void shortcut(Integer key, Runnable runnable) { + this.shortcutKey = key; + this.shortcutAction = runnable; } /** @@ -97,6 +106,9 @@ protected final void init() { * usefull. Typically key and mousebindings are registered from this method. */ protected void initInternal() { + if (shortcutKey != null && shortcutAction != null) { + registerHotKeyBinding(shortcutKey, shortcutAction); + } } @Override diff --git a/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/GridView.java b/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/GridView.java index 7d176d133..5857ef1bb 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/GridView.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/GridView.java @@ -261,6 +261,28 @@ public KeyHandler getKeyHandler() { return super.getKeyHandler(); } + @Override + public KeyHandler getHotKeyHandler() { + log.trace("getHotKeyHandler()"); + KeyHandler handler = null; + for (GridItem i : gridItems) { + if (handler == null) { + handler = i.view.getHotKeyHandler(); + } + else { + handler = handler.thenIfNotConsumed(i.view.getHotKeyHandler()); + } + // handler = i.view.getHotKeyHandler(); + // if (i.view.hasFocus()) { + // handler = i.view.getHotKeyHandler(); + // break; + // } + } + if (handler != null) { + return handler.thenIfNotConsumed(super.getHotKeyHandler()); + } + return super.getHotKeyHandler(); + } @Override public boolean hasFocus() { diff --git a/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/ListView.java b/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/ListView.java index fbe2d1eeb..fa83b53e0 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/ListView.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/ListView.java @@ -100,6 +100,7 @@ public ListView(@Nullable List items, ItemStyle itemStyle) { @Override protected void initInternal() { + super.initInternal(); registerViewCommand(ViewCommand.LINE_UP, () -> up()); registerViewCommand(ViewCommand.LINE_DOWN, () -> down()); diff --git a/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/MenuBarView.java b/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/MenuBarView.java index b34164237..b87550ae6 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/MenuBarView.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/MenuBarView.java @@ -247,9 +247,11 @@ private void closeCurrentMenuView() { private MenuView buildMenuView(MenuBarItem item) { MenuView menuView = new MenuView(item.getItems()); + menuView.init(); menuView.setEventLoop(getEventLoop()); menuView.setThemeResolver(getThemeResolver()); menuView.setThemeName(getThemeName()); + menuView.setViewService(getViewService()); menuView.setShowBorder(true); menuView.setLayer(1); Rectangle rect = getInnerRect(); diff --git a/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/View.java b/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/View.java index 11cbc1ca1..dce078e62 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/View.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/component/view/control/View.java @@ -30,6 +30,8 @@ */ public interface View extends Control { + void init(); + /** * Sets a layer index this {@code View} operates on. * diff --git a/spring-shell-core/src/main/java/org/springframework/shell/component/view/event/KeyBinder.java b/spring-shell-core/src/main/java/org/springframework/shell/component/view/event/KeyBinder.java index 019aa996d..7aef6368d 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/component/view/event/KeyBinder.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/component/view/event/KeyBinder.java @@ -47,6 +47,7 @@ public void bindAll(KeyMap keyMap) { for (char i = KeyEvent.Key.a; i <= KeyEvent.Key.z; i++) { keyMap.bind(i | KeyEvent.KeyMask.AltMask, alt(i)); + keyMap.bind(i | KeyEvent.KeyMask.CtrlMask, ctrl(i)); } keyMap.bind(KeyEvent.Key.q | KeyEvent.KeyMask.CtrlMask, ctrl('q')); diff --git a/spring-shell-core/src/test/java/org/springframework/shell/component/view/control/AbstractViewTests.java b/spring-shell-core/src/test/java/org/springframework/shell/component/view/control/AbstractViewTests.java index 216dc845e..afbd89805 100644 --- a/spring-shell-core/src/test/java/org/springframework/shell/component/view/control/AbstractViewTests.java +++ b/spring-shell-core/src/test/java/org/springframework/shell/component/view/control/AbstractViewTests.java @@ -66,6 +66,7 @@ protected void clearScreens() { } protected void configure(View view) { + view.init(); if (eventLoop != null) { if (view instanceof AbstractView v) { v.setEventLoop(eventLoop); diff --git a/spring-shell-docs/src/test/java/org/springframework/shell/docs/TerminalUiSnippets.java b/spring-shell-docs/src/test/java/org/springframework/shell/docs/TerminalUiSnippets.java index 78dbeb542..e4a3b69ea 100644 --- a/spring-shell-docs/src/test/java/org/springframework/shell/docs/TerminalUiSnippets.java +++ b/spring-shell-docs/src/test/java/org/springframework/shell/docs/TerminalUiSnippets.java @@ -38,6 +38,7 @@ class SampleIntro { void sample() { TerminalUI ui = builder.build(); BoxView view = new BoxView(); + ui.configure(view); view.setDrawFunction((screen, rect) -> { screen.writerBuilder() .build() @@ -59,6 +60,7 @@ class Sample3 { void sample() { TerminalUI ui = new TerminalUI(terminal); BoxView view = new BoxView(); + ui.configure(view); ui.setRoot(view, true); EventLoop eventLoop = ui.getEventLoop(); eventLoop.keyEvents() diff --git a/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/Catalog.java b/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/Catalog.java index f68d17463..7a8ef5375 100644 --- a/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/Catalog.java +++ b/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/Catalog.java @@ -207,11 +207,12 @@ private AppView buildScenarioBrowser(EventLoop eventLoop, TerminalUI component) private ListView buildCategorySelector() { ListView categories = new ListView<>(); + categories.shortcut(Key.a | KeyMask.CtrlMask, () -> {}); ui.configure(categories); List items = List.copyOf(categoryMap.keySet()); categories.setItems(items); - categories.setTitle("Categories"); + categories.setTitle("Categories (CTRL+A)"); categories.setFocusedTitleStyle(ScreenItem.STYLE_BOLD); categories.setShowBorder(true); return categories; @@ -242,8 +243,9 @@ protected void drawContent(Screen screen) { private ListView buildScenarioSelector() { ListView scenarios = new ListView<>(); + scenarios.shortcut(Key.s | KeyMask.CtrlMask, () -> {}); ui.configure(scenarios); - scenarios.setTitle("Scenarios"); + scenarios.setTitle("Scenarios (CTRL+S)"); scenarios.setFocusedTitleStyle(ScreenItem.STYLE_BOLD); scenarios.setShowBorder(true); scenarios.setCellFactory((list, item) -> new ScenarioListCell(item));