Autoconfigure TerminalUI
- Add TerminalUIBuilder which can be used to build TerminalUI - Add TerminalUICustomizer which can customize TerminalUI - What is autoconfigured is TerminalUIBuilder. - In TerminalUI add configure for views which now allows easier way to set needed stuff in views. - Various changes in a catalog app - Fixes #900
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.boot;
|
||||
|
||||
import org.jline.terminal.Terminal;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.shell.component.view.TerminalUI;
|
||||
import org.springframework.shell.component.view.TerminalUIBuilder;
|
||||
import org.springframework.shell.component.view.TerminalUICustomizer;
|
||||
import org.springframework.shell.style.ThemeActive;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass(TerminalUI.class)
|
||||
public class TerminalUIAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
@ConditionalOnMissingBean
|
||||
public TerminalUIBuilder terminalUIBuilder(Terminal terminal, ThemeResolver themeResolver, ThemeActive themeActive,
|
||||
ObjectProvider<TerminalUICustomizer> customizerProvider) {
|
||||
TerminalUIBuilder builder = new TerminalUIBuilder(terminal);
|
||||
builder = builder.themeName(themeActive.get());
|
||||
builder = builder.themeResolver(themeResolver);
|
||||
builder = builder.customizers(customizerProvider.orderedStream().toList());
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,3 +14,4 @@ org.springframework.shell.boot.StandardAPIAutoConfiguration
|
||||
org.springframework.shell.boot.ThemingAutoConfiguration
|
||||
org.springframework.shell.boot.StandardCommandsAutoConfiguration
|
||||
org.springframework.shell.boot.ComponentFlowAutoConfiguration
|
||||
org.springframework.shell.boot.TerminalUIAutoConfiguration
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.boot;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.jline.terminal.Size;
|
||||
import org.jline.terminal.Terminal;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.shell.component.view.TerminalUI;
|
||||
import org.springframework.shell.component.view.TerminalUIBuilder;
|
||||
import org.springframework.shell.component.view.TerminalUICustomizer;
|
||||
import org.springframework.shell.style.ThemeActive;
|
||||
import org.springframework.shell.style.ThemeRegistry;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class TerminalUIAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(TerminalUIAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void terminalUICreated() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(MockConfiguration.class)
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(TerminalUIBuilder.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void canCustomize() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestConfiguration.class, MockConfiguration.class)
|
||||
.run(context -> {
|
||||
TerminalUIBuilder builder = context.getBean(TerminalUIBuilder.class);
|
||||
Set<TerminalUICustomizer> customizers = (Set<TerminalUICustomizer>) ReflectionTestUtils
|
||||
.getField(builder, "customizers");
|
||||
assertThat(customizers).hasSize(1);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class MockConfiguration {
|
||||
|
||||
@Bean
|
||||
Terminal mockTerminal() {
|
||||
Terminal terminal = mock(Terminal.class);
|
||||
when(terminal.getBufferSize()).thenReturn(new Size());
|
||||
return terminal;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ThemeResolver mockThemeResolver() {
|
||||
return new ThemeResolver(new ThemeRegistry(), "default");
|
||||
}
|
||||
|
||||
@Bean
|
||||
ThemeActive themeActive() {
|
||||
return () -> {
|
||||
return "default";
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
TerminalUICustomizer terminalUICustomizer() {
|
||||
return new TestTerminalUICustomizer();
|
||||
}
|
||||
}
|
||||
|
||||
static class TestTerminalUICustomizer implements TerminalUICustomizer {
|
||||
|
||||
@Override
|
||||
public void customize(TerminalUI terminalUI) {
|
||||
terminalUI.setThemeName("test");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -47,6 +47,7 @@ import org.springframework.shell.component.view.event.MouseHandler;
|
||||
import org.springframework.shell.component.view.event.MouseHandler.MouseHandlerResult;
|
||||
import org.springframework.shell.component.view.screen.DefaultScreen;
|
||||
import org.springframework.shell.geom.Rectangle;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -73,6 +74,8 @@ public class TerminalUI implements ViewService {
|
||||
private final KeyBinder keyBinder;
|
||||
private DefaultEventLoop eventLoop = new DefaultEventLoop();
|
||||
private View focus = null;
|
||||
private ThemeResolver themeResolver;
|
||||
private String themeName = "default";
|
||||
|
||||
/**
|
||||
* Constructs a handler with a given terminal.
|
||||
@@ -135,6 +138,64 @@ public class TerminalUI implements ViewService {
|
||||
getEventLoop().dispatch(ShellMessageBuilder.ofRedraw());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link ThemeResolver}.
|
||||
*
|
||||
* @param themeResolver the theme resolver
|
||||
*/
|
||||
public void setThemeResolver(ThemeResolver themeResolver) {
|
||||
this.themeResolver = themeResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link ThemeResolver}.
|
||||
*
|
||||
* @return a theme resolver
|
||||
*/
|
||||
public ThemeResolver getThemeResolver() {
|
||||
return themeResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@code theme name}.
|
||||
*
|
||||
* @param themeName the theme name
|
||||
*/
|
||||
public void setThemeName(String themeName) {
|
||||
this.themeName = themeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@code theme name}.
|
||||
*
|
||||
* @return a theme name
|
||||
*/
|
||||
public String getThemeName() {
|
||||
return themeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link ViewService}.
|
||||
*
|
||||
* @return a view service
|
||||
*/
|
||||
public ViewService getViewService() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure view for {@link EventLoop}, {@link ThemeResolver},
|
||||
* {@code theme name} and {@link ViewService}.
|
||||
*
|
||||
* @param view the view to configure
|
||||
*/
|
||||
public void configure(View view) {
|
||||
view.setEventLoop(eventLoop);
|
||||
view.setThemeResolver(themeResolver);
|
||||
view.setThemeName(themeName);
|
||||
view.setViewService(getViewService());
|
||||
}
|
||||
|
||||
public void setFocus(@Nullable View view) {
|
||||
if (focus != null) {
|
||||
focus.focus(focus, false);
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.component.view;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jline.terminal.Terminal;
|
||||
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Builder that can be used to configure and create a {@link TerminalUI}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public class TerminalUIBuilder {
|
||||
|
||||
private final Terminal terminal;
|
||||
private final Set<TerminalUICustomizer> customizers;
|
||||
private final ThemeResolver themeResolver;
|
||||
private final String themeName;
|
||||
|
||||
/**
|
||||
* Create a new {@link TerminalUIBuilder} instance.
|
||||
*
|
||||
* @param terminal the terminal
|
||||
* @param customizers any {@link TerminalUICustomizer TerminalUICustomizers}
|
||||
* that should be applied when the {@link TerminalUI} is
|
||||
* built
|
||||
*/
|
||||
public TerminalUIBuilder(Terminal terminal, TerminalUICustomizer... customizers) {
|
||||
this.terminal = terminal;
|
||||
this.customizers = copiedSetOf(customizers);
|
||||
this.themeResolver = null;
|
||||
this.themeName = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TerminalUIBuilder} instance.
|
||||
*
|
||||
* @param terminal the terminal
|
||||
* @param customizers any {@link TerminalUICustomizer TerminalUICustomizers}
|
||||
* that should be applied when the {@link TerminalUI} is
|
||||
* built
|
||||
* @param themeResolver the theme resolver
|
||||
* @param themeName the theme name
|
||||
*/
|
||||
public TerminalUIBuilder(Terminal terminal, Set<TerminalUICustomizer> customizers, ThemeResolver themeResolver,
|
||||
String themeName) {
|
||||
this.terminal = terminal;
|
||||
this.customizers = customizers;
|
||||
this.themeResolver = themeResolver;
|
||||
this.themeName = themeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link ThemeResolver} for {@link TerminalUI} to build.
|
||||
*
|
||||
* @param themeResolver the theme resolver
|
||||
* @return a new builder instance
|
||||
*/
|
||||
public TerminalUIBuilder themeResolver(ThemeResolver themeResolver) {
|
||||
return new TerminalUIBuilder(terminal, customizers, themeResolver, themeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@code theme name} for {@link TerminalUI} to build.
|
||||
*
|
||||
* @param themeName the theme name
|
||||
* @return a new builder instance
|
||||
*/
|
||||
public TerminalUIBuilder themeName(String themeName) {
|
||||
return new TerminalUIBuilder(terminal, customizers, themeResolver, themeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link TerminalUICustomizer TerminalUICustomizer} that should be
|
||||
* applied to the {@link TerminalUI}. Customizers are applied in the order that they
|
||||
* were added after builder configuration has been applied. Setting this value will
|
||||
* replace any previously configured customizers.
|
||||
*
|
||||
* @param customizers the customizers to set
|
||||
* @return a new builder instance
|
||||
*/
|
||||
public TerminalUIBuilder customizers(Collection<? extends TerminalUICustomizer> customizers) {
|
||||
Assert.notNull(customizers, "Customizers must not be null");
|
||||
return new TerminalUIBuilder(terminal, copiedSetOf(customizers), themeResolver, themeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new {@link TerminalUI} instance and configure it using this builder.
|
||||
*
|
||||
* @return a configured {@link TerminalUI} instance.
|
||||
*/
|
||||
public TerminalUI build() {
|
||||
return configure(new TerminalUI(terminal));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the provided {@link TerminalUI} instance using this builder.
|
||||
*
|
||||
* @param <T> the type of terminal ui
|
||||
* @param terminalUI the {@link TerminalUI} to configure
|
||||
* @return the terminal ui instance
|
||||
*/
|
||||
public <T extends TerminalUI> T configure(T terminalUI) {
|
||||
if (themeResolver != null) {
|
||||
terminalUI.setThemeResolver(themeResolver);
|
||||
}
|
||||
if (StringUtils.hasText(themeName)) {
|
||||
terminalUI.setThemeName(themeName);
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(customizers)) {
|
||||
for (TerminalUICustomizer customizer : customizers) {
|
||||
customizer.customize(terminalUI);
|
||||
}
|
||||
}
|
||||
return terminalUI;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Set<T> copiedSetOf(T... items) {
|
||||
return copiedSetOf(Arrays.asList(items));
|
||||
}
|
||||
|
||||
private <T> Set<T> copiedSetOf(Collection<? extends T> collection) {
|
||||
return Collections.unmodifiableSet(new LinkedHashSet<>(collection));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.component.view;
|
||||
|
||||
/**
|
||||
* Callback interface that can be used to customize a {@link TerminalUI}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
* @see TerminalUIBuilder
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface TerminalUICustomizer {
|
||||
|
||||
/**
|
||||
* Callback to customize a {@link TerminalUI} instance.
|
||||
*
|
||||
* @param terminalUI the terminal ui to customize
|
||||
*/
|
||||
void customize(TerminalUI terminalUI);
|
||||
|
||||
}
|
||||
@@ -263,6 +263,7 @@ public abstract class AbstractView extends AbstractControl implements View {
|
||||
*
|
||||
* @param viewService the view service
|
||||
*/
|
||||
@Override
|
||||
public void setViewService(ViewService viewService) {
|
||||
this.viewService = viewService;
|
||||
}
|
||||
|
||||
@@ -89,6 +89,13 @@ public interface View extends Control {
|
||||
*/
|
||||
void setEventLoop(@Nullable EventLoop eventLoop);
|
||||
|
||||
/**
|
||||
* Sets a {@link ViewService}.
|
||||
*
|
||||
* @param viewService the view service
|
||||
*/
|
||||
void setViewService(ViewService viewService);
|
||||
|
||||
/**
|
||||
* Get supported commands.
|
||||
*
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.jline.terminal.Terminal;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -29,6 +28,7 @@ import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.shell.component.message.ShellMessageBuilder;
|
||||
import org.springframework.shell.component.view.TerminalUI;
|
||||
import org.springframework.shell.component.view.TerminalUIBuilder;
|
||||
import org.springframework.shell.component.view.control.AppView;
|
||||
import org.springframework.shell.component.view.control.AppView.AppViewEvent;
|
||||
import org.springframework.shell.component.view.control.BoxView;
|
||||
@@ -52,10 +52,10 @@ import org.springframework.shell.component.view.event.KeyEvent.Key;
|
||||
import org.springframework.shell.component.view.event.KeyEvent.KeyMask;
|
||||
import org.springframework.shell.component.view.screen.Screen;
|
||||
import org.springframework.shell.component.view.screen.Screen.Writer;
|
||||
import org.springframework.shell.component.view.screen.ScreenItem;
|
||||
import org.springframework.shell.geom.HorizontalAlign;
|
||||
import org.springframework.shell.geom.Rectangle;
|
||||
import org.springframework.shell.geom.VerticalAlign;
|
||||
import org.springframework.shell.component.view.screen.ScreenItem;
|
||||
import org.springframework.shell.samples.catalog.scenario.Scenario;
|
||||
import org.springframework.shell.samples.catalog.scenario.ScenarioComponent;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
@@ -79,7 +79,6 @@ public class Catalog {
|
||||
|
||||
// mapping from category name to scenarios(can belong to multiple categories)
|
||||
private final Map<String, List<ScenarioData>> categoryMap = new TreeMap<>();
|
||||
private final Terminal terminal;
|
||||
private View currentScenarioView = null;
|
||||
private TerminalUI ui;
|
||||
private ListView<String> categories;
|
||||
@@ -88,9 +87,10 @@ public class Catalog {
|
||||
private EventLoop eventLoop;
|
||||
private ThemeResolver themeResolver;
|
||||
private String activeThemeName = "default";
|
||||
private TerminalUIBuilder terminalUIBuilder;
|
||||
|
||||
public Catalog(Terminal terminal, List<Scenario> scenarios, ThemeResolver themeResolver) {
|
||||
this.terminal = terminal;
|
||||
public Catalog(TerminalUIBuilder terminalUIBuilder, ThemeResolver themeResolver, List<Scenario> scenarios) {
|
||||
this.terminalUIBuilder = terminalUIBuilder;
|
||||
this.themeResolver = themeResolver;
|
||||
mapScenarios(scenarios);
|
||||
}
|
||||
@@ -122,11 +122,9 @@ public class Catalog {
|
||||
* Main run loop. Builds the ui and exits when user requests exit.
|
||||
*/
|
||||
public void run() {
|
||||
ui = new TerminalUI(terminal);
|
||||
ui = terminalUIBuilder.build();
|
||||
eventLoop = ui.getEventLoop();
|
||||
app = buildScenarioBrowser(eventLoop, ui);
|
||||
app.setThemeResolver(themeResolver);
|
||||
app.setThemeName(activeThemeName);
|
||||
|
||||
// handle logic to switch between main scenario browser
|
||||
// and currently active scenario
|
||||
@@ -155,14 +153,12 @@ public class Catalog {
|
||||
private AppView buildScenarioBrowser(EventLoop eventLoop, TerminalUI component) {
|
||||
// category selector on left, scenario selector on right
|
||||
GridView grid = new GridView();
|
||||
grid.setThemeResolver(themeResolver);
|
||||
grid.setThemeName(activeThemeName);
|
||||
grid.setEventLoop(eventLoop);
|
||||
component.configure(grid);
|
||||
grid.setRowSize(0);
|
||||
grid.setColumnSize(30, 0);
|
||||
|
||||
categories = buildCategorySelector(eventLoop);
|
||||
scenarios = buildScenarioSelector(eventLoop);
|
||||
categories = buildCategorySelector();
|
||||
scenarios = buildScenarioSelector();
|
||||
|
||||
grid.addItem(categories, 0, 0, 1, 1, 0, 0);
|
||||
grid.addItem(scenarios, 0, 1, 1, 1, 0, 0);
|
||||
@@ -172,16 +168,16 @@ public class Catalog {
|
||||
|
||||
// we use main app view to represent scenario browser
|
||||
AppView app = new AppView(grid, menuBar, statusBar);
|
||||
app.setThemeResolver(themeResolver);
|
||||
app.setThemeName(activeThemeName);
|
||||
app.setEventLoop(eventLoop);
|
||||
component.configure(app);
|
||||
|
||||
// handle event when scenario is chosen
|
||||
eventLoop.onDestroy(eventLoop.viewEvents(LISTVIEW_SCENARIO_TYPEREF, scenarios)
|
||||
.subscribe(event -> {
|
||||
View view = event.args().item().scenario()
|
||||
.configure(ui, eventLoop, themeResolver, activeThemeName)
|
||||
.build();
|
||||
View view = event.args().item().scenario().configure(ui).build();
|
||||
ui.configure(view);
|
||||
// View view = event.args().item().scenario()
|
||||
// .configure(ui, eventLoop, themeResolver, activeThemeName)
|
||||
// .build();
|
||||
component.setRoot(view, true);
|
||||
currentScenarioView = view;
|
||||
}));
|
||||
@@ -225,11 +221,10 @@ public class Catalog {
|
||||
return app;
|
||||
}
|
||||
|
||||
private ListView<String> buildCategorySelector(EventLoop eventLoop) {
|
||||
private ListView<String> buildCategorySelector() {
|
||||
ListView<String> categories = new ListView<>();
|
||||
categories.setThemeResolver(themeResolver);
|
||||
categories.setThemeName(activeThemeName);
|
||||
categories.setEventLoop(eventLoop);
|
||||
ui.configure(categories);
|
||||
|
||||
List<String> items = List.copyOf(categoryMap.keySet());
|
||||
categories.setItems(items);
|
||||
categories.setTitle("Categories");
|
||||
@@ -261,11 +256,9 @@ public class Catalog {
|
||||
}
|
||||
}
|
||||
|
||||
private ListView<ScenarioData> buildScenarioSelector(EventLoop eventLoop) {
|
||||
private ListView<ScenarioData> buildScenarioSelector() {
|
||||
ListView<ScenarioData> scenarios = new ListView<>();
|
||||
scenarios.setThemeResolver(themeResolver);
|
||||
scenarios.setThemeName(activeThemeName);
|
||||
scenarios.setEventLoop(eventLoop);
|
||||
ui.configure(scenarios);
|
||||
scenarios.setTitle("Scenarios");
|
||||
scenarios.setFocusedTitleStyle(ScreenItem.STYLE_BOLD);
|
||||
scenarios.setShowBorder(true);
|
||||
@@ -287,9 +280,7 @@ public class Catalog {
|
||||
|
||||
private DialogView buildAboutDialog() {
|
||||
ButtonView button = new ButtonView("OK");
|
||||
button.setThemeResolver(themeResolver);
|
||||
button.setThemeName(activeThemeName);
|
||||
button.setEventLoop(eventLoop);
|
||||
ui.configure(button);
|
||||
|
||||
BoxView content = new BoxView();
|
||||
content.setDrawFunction((screen, rect) -> {
|
||||
@@ -297,11 +288,7 @@ public class Catalog {
|
||||
return rect;
|
||||
});
|
||||
DialogView dialog = new DialogView(content, button);
|
||||
dialog.setThemeResolver(themeResolver);
|
||||
dialog.setThemeName(activeThemeName);
|
||||
dialog.setEventLoop(eventLoop);
|
||||
dialog.setViewService(ui);
|
||||
|
||||
ui.configure(dialog);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@@ -329,18 +316,14 @@ public class Catalog {
|
||||
MenuItem.of("About", MenuItemCheckStyle.NOCHECK, aboutAction))
|
||||
);
|
||||
|
||||
menuBar.setThemeResolver(themeResolver);
|
||||
menuBar.setThemeName(activeThemeName);
|
||||
menuBar.setEventLoop(eventLoop);
|
||||
ui.configure(menuBar);
|
||||
return menuBar;
|
||||
}
|
||||
|
||||
private StatusBarView buildStatusBar(EventLoop eventLoop) {
|
||||
Runnable quitAction = () -> requestQuit();
|
||||
StatusBarView statusBar = new StatusBarView();
|
||||
statusBar.setThemeResolver(themeResolver);
|
||||
statusBar.setThemeName(activeThemeName);
|
||||
statusBar.setEventLoop(eventLoop);
|
||||
ui.configure(statusBar);
|
||||
StatusItem item1 = new StatusBarView.StatusItem("CTRL-Q Quit", quitAction);
|
||||
StatusItem item2 = new StatusBarView.StatusItem("F10 Status Bar");
|
||||
statusBar.setItems(Arrays.asList(item1, item2));
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.shell.samples.catalog;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.command.annotation.Command;
|
||||
import org.springframework.shell.component.view.TerminalUIBuilder;
|
||||
import org.springframework.shell.samples.catalog.scenario.Scenario;
|
||||
import org.springframework.shell.standard.AbstractShellComponent;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
@@ -31,16 +32,18 @@ import org.springframework.shell.style.ThemeResolver;
|
||||
public class CatalogCommand extends AbstractShellComponent {
|
||||
|
||||
private final List<Scenario> scenarios;
|
||||
private final TerminalUIBuilder terminalUIBuilder;
|
||||
private final ThemeResolver themeResolver;
|
||||
|
||||
public CatalogCommand(List<Scenario> scenarios, ThemeResolver themeResolver) {
|
||||
public CatalogCommand(List<Scenario> scenarios, TerminalUIBuilder terminalUIBuilder, ThemeResolver themeResolver) {
|
||||
this.scenarios = scenarios;
|
||||
this.terminalUIBuilder = terminalUIBuilder;
|
||||
this.themeResolver = themeResolver;
|
||||
}
|
||||
|
||||
@Command(command = "catalog")
|
||||
public void catalog() {
|
||||
Catalog catalog = new Catalog(getTerminal(), scenarios, themeResolver);
|
||||
Catalog catalog = new Catalog(terminalUIBuilder, themeResolver, scenarios);
|
||||
catalog.run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
*/
|
||||
package org.springframework.shell.samples.catalog.scenario;
|
||||
|
||||
import org.springframework.shell.component.view.control.AbstractView;
|
||||
import org.springframework.shell.component.view.TerminalUI;
|
||||
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;
|
||||
@@ -27,20 +28,12 @@ import org.springframework.shell.style.ThemeResolver;
|
||||
*/
|
||||
public abstract class AbstractScenario implements Scenario {
|
||||
|
||||
private TerminalUI ui;
|
||||
private ViewService viewService;
|
||||
private EventLoop eventloop;
|
||||
private ThemeResolver themeResolver;
|
||||
private String themeName;
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
@@ -57,9 +50,23 @@ public abstract class AbstractScenario implements Scenario {
|
||||
return themeName;
|
||||
}
|
||||
|
||||
protected void configure(AbstractView view) {
|
||||
view.setEventLoop(getEventloop());
|
||||
view.setThemeResolver(getThemeResolver());
|
||||
view.setThemeName(getThemeName());
|
||||
protected TerminalUI getTerminalUI() {
|
||||
return ui;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scenario configure(TerminalUI ui) {
|
||||
this.ui = ui;
|
||||
this.themeName = ui.getThemeName();
|
||||
this.themeResolver = ui.getThemeResolver();
|
||||
this.eventloop = ui.getEventLoop();
|
||||
this.viewService = ui.getViewService();
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void configure(View view) {
|
||||
if (ui != null) {
|
||||
ui.configure(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,8 @@
|
||||
*/
|
||||
package org.springframework.shell.samples.catalog.scenario;
|
||||
|
||||
import org.springframework.shell.component.view.TerminalUI;
|
||||
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;
|
||||
|
||||
/**
|
||||
* {@link Scenario} participates in a catalog showcase.
|
||||
@@ -44,11 +42,9 @@ public interface Scenario {
|
||||
/**
|
||||
* Configure scenario.
|
||||
*
|
||||
* @param eventloop eventloop for scenario
|
||||
* @param themeResolver theme resolver for scenario
|
||||
* @param themeName theme name for scenario
|
||||
* @param ui the terminal ui
|
||||
* @return scenario for chaining
|
||||
*/
|
||||
Scenario configure(ViewService viewService, EventLoop eventloop, ThemeResolver themeResolver, String themeName);
|
||||
Scenario configure(TerminalUI ui);
|
||||
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public class CheckedListViewScenario extends AbstractScenario {
|
||||
@Override
|
||||
public View build() {
|
||||
ListView<String> view = new ListView<>(ItemStyle.RADIO);
|
||||
view.setEventLoop(getEventloop());
|
||||
configure(view);
|
||||
view.setItems(Arrays.asList("item1", "item2", "item3"));
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public class LongListViewScenario extends AbstractScenario {
|
||||
@Override
|
||||
public View build() {
|
||||
ListView<String> view = new ListView<>();
|
||||
view.setEventLoop(getEventloop());
|
||||
configure(view);
|
||||
List<String> items = IntStream.of(20).mapToObj(i -> "item" + i).collect(Collectors.toList());
|
||||
view.setItems(items);
|
||||
return view;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class RadioListViewScenario extends AbstractScenario {
|
||||
@Override
|
||||
public View build() {
|
||||
ListView<String> view = new ListView<>(ItemStyle.CHECKED);
|
||||
view.setEventLoop(getEventloop());
|
||||
configure(view);
|
||||
view.setItems(Arrays.asList("item1", "item2", "item3"));
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class SimpleListViewScenario extends AbstractScenario {
|
||||
@Override
|
||||
public View build() {
|
||||
ListView<String> view = new ListView<>();
|
||||
view.setEventLoop(getEventloop());
|
||||
configure(view);
|
||||
view.setItems(Arrays.asList("item1", "item2"));
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,6 @@ public class DialogScenario extends AbstractScenario {
|
||||
});
|
||||
DialogView dialog = new DialogView(content, button);
|
||||
configure(dialog);
|
||||
dialog.setViewService(getViewService());
|
||||
return dialog;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ public class SimpleInputViewScenario extends AbstractScenario {
|
||||
@Override
|
||||
public View build() {
|
||||
InputView view = new InputView();
|
||||
view.setEventLoop(getEventloop());
|
||||
configure(view);
|
||||
view.setTitle("Input");
|
||||
view.setShowBorder(true);
|
||||
return view;
|
||||
|
||||
Reference in New Issue
Block a user