Skip to content

Commit 2d29050

Browse files
committed
Make Catalog browsers focusable with shortcuts
- Category and scenario list views can now be focused with ctrl+a/ctrl+s. - Bind all keys with ctrl modifier - Add "shortcut" concept to views which currently hooks to hot keys. - In a catalog app by using a "shortcut" then takes the key event as a hot key, as it consumes resulting behaviour is to focus. - Move view initInternal away from constructor call to require user to call init() which is not in a View interface - Relates #826
1 parent a9b1675 commit 2d29050

File tree

10 files changed

+53
-7
lines changed

10 files changed

+53
-7
lines changed

spring-shell-core/src/main/java/org/springframework/shell/component/view/TerminalUI.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ public ViewService getViewService() {
190190
* @param view the view to configure
191191
*/
192192
public void configure(View view) {
193+
view.init();
193194
view.setEventLoop(eventLoop);
194195
view.setThemeResolver(themeResolver);
195196
view.setThemeName(themeName);

spring-shell-core/src/main/java/org/springframework/shell/component/view/control/AbstractView.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,7 @@ public abstract class AbstractView extends AbstractControl implements View {
6060
private Map<Integer, KeyBindingValue> keyBindings = new HashMap<>();
6161
private Map<Integer, KeyBindingValue> hotKeyBindings = new HashMap<>();
6262
private Map<Integer, MouseBindingValue> mouseBindings = new HashMap<>();
63-
64-
public AbstractView() {
65-
init();
66-
}
63+
private boolean init = false;
6764

6865
/**
6966
* Register {@link Disposable} to get disposed when view terminates.
@@ -88,15 +85,30 @@ public void destroy() {
8885
*
8986
* @see #initInternal()
9087
*/
91-
protected final void init() {
88+
@Override
89+
public final void init() {
90+
if (init) {
91+
return;
92+
}
9293
initInternal();
94+
init = true;
95+
}
96+
97+
private Integer shortcutKey;
98+
private Runnable shortcutAction;
99+
public void shortcut(Integer key, Runnable runnable) {
100+
this.shortcutKey = key;
101+
this.shortcutAction = runnable;
93102
}
94103

95104
/**
96105
* Internal init method called from {@link #init()}. Override to do something
97106
* usefull. Typically key and mousebindings are registered from this method.
98107
*/
99108
protected void initInternal() {
109+
if (shortcutKey != null && shortcutAction != null) {
110+
registerHotKeyBinding(shortcutKey, shortcutAction);
111+
}
100112
}
101113

102114
@Override

spring-shell-core/src/main/java/org/springframework/shell/component/view/control/GridView.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,28 @@ public KeyHandler getKeyHandler() {
261261
return super.getKeyHandler();
262262
}
263263

264+
@Override
265+
public KeyHandler getHotKeyHandler() {
266+
log.trace("getHotKeyHandler()");
267+
KeyHandler handler = null;
268+
for (GridItem i : gridItems) {
269+
if (handler == null) {
270+
handler = i.view.getHotKeyHandler();
271+
}
272+
else {
273+
handler = handler.thenIfNotConsumed(i.view.getHotKeyHandler());
274+
}
275+
// handler = i.view.getHotKeyHandler();
276+
// if (i.view.hasFocus()) {
277+
// handler = i.view.getHotKeyHandler();
278+
// break;
279+
// }
280+
}
281+
if (handler != null) {
282+
return handler.thenIfNotConsumed(super.getHotKeyHandler());
283+
}
284+
return super.getHotKeyHandler();
285+
}
264286

265287
@Override
266288
public boolean hasFocus() {

spring-shell-core/src/main/java/org/springframework/shell/component/view/control/ListView.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ public ListView(@Nullable List<T> items, ItemStyle itemStyle) {
100100

101101
@Override
102102
protected void initInternal() {
103+
super.initInternal();
103104
registerViewCommand(ViewCommand.LINE_UP, () -> up());
104105
registerViewCommand(ViewCommand.LINE_DOWN, () -> down());
105106

spring-shell-core/src/main/java/org/springframework/shell/component/view/control/MenuBarView.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,11 @@ private void closeCurrentMenuView() {
247247

248248
private MenuView buildMenuView(MenuBarItem item) {
249249
MenuView menuView = new MenuView(item.getItems());
250+
menuView.init();
250251
menuView.setEventLoop(getEventLoop());
251252
menuView.setThemeResolver(getThemeResolver());
252253
menuView.setThemeName(getThemeName());
254+
menuView.setViewService(getViewService());
253255
menuView.setShowBorder(true);
254256
menuView.setLayer(1);
255257
Rectangle rect = getInnerRect();

spring-shell-core/src/main/java/org/springframework/shell/component/view/control/View.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
*/
3131
public interface View extends Control {
3232

33+
void init();
34+
3335
/**
3436
* Sets a layer index this {@code View} operates on.
3537
*

spring-shell-core/src/main/java/org/springframework/shell/component/view/event/KeyBinder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public void bindAll(KeyMap<Integer> keyMap) {
4747

4848
for (char i = KeyEvent.Key.a; i <= KeyEvent.Key.z; i++) {
4949
keyMap.bind(i | KeyEvent.KeyMask.AltMask, alt(i));
50+
keyMap.bind(i | KeyEvent.KeyMask.CtrlMask, ctrl(i));
5051
}
5152

5253
keyMap.bind(KeyEvent.Key.q | KeyEvent.KeyMask.CtrlMask, ctrl('q'));

spring-shell-core/src/test/java/org/springframework/shell/component/view/control/AbstractViewTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ protected void clearScreens() {
6666
}
6767

6868
protected void configure(View view) {
69+
view.init();
6970
if (eventLoop != null) {
7071
if (view instanceof AbstractView v) {
7172
v.setEventLoop(eventLoop);

spring-shell-docs/src/test/java/org/springframework/shell/docs/TerminalUiSnippets.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class SampleIntro {
3838
void sample() {
3939
TerminalUI ui = builder.build();
4040
BoxView view = new BoxView();
41+
ui.configure(view);
4142
view.setDrawFunction((screen, rect) -> {
4243
screen.writerBuilder()
4344
.build()
@@ -59,6 +60,7 @@ class Sample3 {
5960
void sample() {
6061
TerminalUI ui = new TerminalUI(terminal);
6162
BoxView view = new BoxView();
63+
ui.configure(view);
6264
ui.setRoot(view, true);
6365
EventLoop eventLoop = ui.getEventLoop();
6466
eventLoop.keyEvents()

spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/Catalog.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,11 +207,12 @@ private AppView buildScenarioBrowser(EventLoop eventLoop, TerminalUI component)
207207

208208
private ListView<String> buildCategorySelector() {
209209
ListView<String> categories = new ListView<>();
210+
categories.shortcut(Key.a | KeyMask.CtrlMask, () -> {});
210211
ui.configure(categories);
211212

212213
List<String> items = List.copyOf(categoryMap.keySet());
213214
categories.setItems(items);
214-
categories.setTitle("Categories");
215+
categories.setTitle("Categories (CTRL+A)");
215216
categories.setFocusedTitleStyle(ScreenItem.STYLE_BOLD);
216217
categories.setShowBorder(true);
217218
return categories;
@@ -242,8 +243,9 @@ protected void drawContent(Screen screen) {
242243

243244
private ListView<ScenarioData> buildScenarioSelector() {
244245
ListView<ScenarioData> scenarios = new ListView<>();
246+
scenarios.shortcut(Key.s | KeyMask.CtrlMask, () -> {});
245247
ui.configure(scenarios);
246-
scenarios.setTitle("Scenarios");
248+
scenarios.setTitle("Scenarios (CTRL+S)");
247249
scenarios.setFocusedTitleStyle(ScreenItem.STYLE_BOLD);
248250
scenarios.setShowBorder(true);
249251
scenarios.setCellFactory((list, item) -> new ScenarioListCell(item));

0 commit comments

Comments
 (0)