From 29417d495b951efeaf1bf9e1911255c58749f37b Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Mon, 21 Aug 2023 09:37:05 +0300 Subject: [PATCH] Initial modal integration - Integrate TerminalUI to ViewService and modals - New Dialog scenario - Relates #825 --- .../shell/component/view/TerminalUI.java | 31 +++++++--- .../shell/samples/catalog/Catalog.java | 8 ++- .../catalog/scenario/AbstractScenario.java | 9 ++- .../samples/catalog/scenario/Scenario.java | 3 +- .../scenario/other/DialogScenario.java | 59 +++++++++++++++++++ 5 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/scenario/other/DialogScenario.java 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 1251d425..6dff16b3 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 @@ -35,6 +35,7 @@ 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; import org.springframework.shell.component.view.event.EventLoop; import org.springframework.shell.component.view.event.KeyBinder; @@ -60,7 +61,7 @@ import org.springframework.util.StringUtils; * * @author Janne Valkealahti */ -public class TerminalUI { +public class TerminalUI implements ViewService { private final static Logger log = LoggerFactory.getLogger(TerminalUI.class); private final Terminal terminal; @@ -70,6 +71,7 @@ public class TerminalUI { private Display display; private Size size; private View rootView; + private View modalView; private boolean fullScreen; private final KeyBinder keyBinder; private DefaultEventLoop eventLoop = new DefaultEventLoop(); @@ -87,6 +89,16 @@ public class TerminalUI { this.keyBinder = new KeyBinder(terminal); } + @Override + public View getModal() { + return modalView; + } + + @Override + public void setModal(View view) { + this.modalView = view; + } + /** * Sets a root view. * @@ -137,11 +149,15 @@ public class TerminalUI { } private void render(int rows, int columns) { - if (rootView == null) { - return; + if (rootView != null) { + rootView.setRect(0, 0, columns, rows); + rootView.draw(virtualDisplay); + } + if (modalView != null) { + modalView.setLayer(1); + modalView.setRect(0, 0, columns, rows); + modalView.draw(virtualDisplay); } - rootView.setRect(0, 0, columns, rows); - rootView.draw(virtualDisplay); } private void display() { @@ -254,8 +270,9 @@ public class TerminalUI { private void handleMouseEvent(MouseEvent event) { log.trace("handleMouseEvent {}", event); - if (rootView != null) { - MouseHandler handler = rootView.getMouseHandler(); + View view = modalView != null ? modalView : rootView; + if (view != null) { + MouseHandler handler = view.getMouseHandler(); if (handler != null) { MouseHandlerResult result = handler.handle(MouseHandler.argsOf(event)); if (result.focus() != null) { 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 57f9173f..f69ee675 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 @@ -180,7 +180,7 @@ public class Catalog { eventLoop.onDestroy(eventLoop.viewEvents(LISTVIEW_SCENARIO_TYPEREF, scenarios) .subscribe(event -> { View view = event.args().item().scenario() - .configure(eventLoop, themeResolver, activeThemeName) + .configure(ui, eventLoop, themeResolver, activeThemeName) .build(); component.setRoot(view, true); currentScenarioView = view; @@ -273,8 +273,12 @@ public class Catalog { return () -> setStyle(style); } + private void about() { + } + private MenuBarView buildMenuBar(EventLoop eventLoop) { Runnable quitAction = () -> requestQuit(); + Runnable aboutAction = () -> about(); MenuItem[] themeItems = themeResolver.themeNames().stream() .map(tn -> { return MenuItem.of(tn, MenuItemCheckStyle.RADIO, styleAction(tn), "default".equals(tn)); @@ -288,7 +292,7 @@ public class Catalog { themeItems) .setHotKey(Key.t | KeyMask.AltMask), MenuBarItem.of("Help", - MenuItem.of("About")) + MenuItem.of("About", MenuItemCheckStyle.NOCHECK, aboutAction)) ); menuBar.setThemeResolver(themeResolver); diff --git a/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/scenario/AbstractScenario.java b/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/scenario/AbstractScenario.java index 1fdb5d1f..caf31f25 100644 --- a/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/scenario/AbstractScenario.java +++ b/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/scenario/AbstractScenario.java @@ -16,6 +16,7 @@ package org.springframework.shell.samples.catalog.scenario; import org.springframework.shell.component.view.control.AbstractView; +import org.springframework.shell.component.view.control.ViewService; import org.springframework.shell.component.view.event.EventLoop; import org.springframework.shell.style.ThemeResolver; @@ -26,18 +27,24 @@ import org.springframework.shell.style.ThemeResolver; */ public abstract class AbstractScenario implements Scenario { + private ViewService viewService; private EventLoop eventloop; private ThemeResolver themeResolver; private String themeName; @Override - public Scenario configure(EventLoop eventloop, ThemeResolver themeResolver, String themeName) { + public Scenario configure(ViewService viewService, EventLoop eventloop, ThemeResolver themeResolver, String themeName) { + this.viewService = viewService; this.eventloop = eventloop; this.themeResolver = themeResolver; this.themeName = themeName; return this; } + protected ViewService getViewService() { + return viewService; + } + protected EventLoop getEventloop() { return eventloop; } diff --git a/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/scenario/Scenario.java b/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/scenario/Scenario.java index 42c7abd7..3b7e223d 100644 --- a/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/scenario/Scenario.java +++ b/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/scenario/Scenario.java @@ -16,6 +16,7 @@ package org.springframework.shell.samples.catalog.scenario; import org.springframework.shell.component.view.control.View; +import org.springframework.shell.component.view.control.ViewService; import org.springframework.shell.component.view.event.EventLoop; import org.springframework.shell.style.ThemeResolver; @@ -48,6 +49,6 @@ public interface Scenario { * @param themeName theme name for scenario * @return scenario for chaining */ - Scenario configure(EventLoop eventloop, ThemeResolver themeResolver, String themeName); + Scenario configure(ViewService viewService, EventLoop eventloop, ThemeResolver themeResolver, String themeName); } diff --git a/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/scenario/other/DialogScenario.java b/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/scenario/other/DialogScenario.java new file mode 100644 index 00000000..d18026b5 --- /dev/null +++ b/spring-shell-samples/spring-shell-sample-catalog/src/main/java/org/springframework/shell/samples/catalog/scenario/other/DialogScenario.java @@ -0,0 +1,59 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.shell.samples.catalog.scenario.other; + +import org.springframework.shell.component.view.control.BoxView; +import org.springframework.shell.component.view.control.ButtonView; +import org.springframework.shell.component.view.control.DialogView; +import org.springframework.shell.component.view.control.View; +import org.springframework.shell.component.view.geom.HorizontalAlign; +import org.springframework.shell.component.view.geom.VerticalAlign; +import org.springframework.shell.samples.catalog.scenario.AbstractScenario; +import org.springframework.shell.samples.catalog.scenario.Scenario; +import org.springframework.shell.samples.catalog.scenario.ScenarioComponent; + +@ScenarioComponent(name = "Dialog", description = "Modal dialog", category = { + Scenario.CATEGORY_OTHER }) +public class DialogScenario extends AbstractScenario { + + private Runnable dialogAction = () -> { + DialogView dialog = buildDialog(); + getViewService().setModal(dialog); + }; + + @Override + public View build() { + ButtonView button = new ButtonView("Open Dialog", dialogAction); + configure(button); + return button; + } + + private DialogView buildDialog() { + ButtonView button = new ButtonView("OK"); + configure(button); + BoxView content = new BoxView(); + content.setDrawFunction((screen, rect) -> { + screen.writerBuilder().layer(1).build().text("Hello World", rect, HorizontalAlign.CENTER, VerticalAlign.CENTER); + return rect; + }); + DialogView dialog = new DialogView(content, button); + configure(dialog); + dialog.setTransparent(false); + dialog.setViewService(getViewService()); + return dialog; + } + +}