Base theming for views
- Control is now aware of active theme name and resolver. - Theme some settings in BoxView, ListView and MenuView. - Overhaul scenario system to make it work with themes. - Add new "background" StyleSetting. - Relates #824
This commit is contained in:
@@ -53,6 +53,7 @@ import org.springframework.shell.component.view.screen.Screen.Writer;
|
||||
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;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -77,10 +78,15 @@ public class Catalog {
|
||||
private View currentScenarioView = null;
|
||||
private TerminalUI ui;
|
||||
private ListView<String> categories;
|
||||
private ListView<ScenarioData> scenarios;
|
||||
private AppView app;
|
||||
private EventLoop eventLoop;
|
||||
private ThemeResolver themeResolver;
|
||||
private String activeThemeName = "default";
|
||||
|
||||
public Catalog(Terminal terminal, List<Scenario> scenarios) {
|
||||
public Catalog(Terminal terminal, List<Scenario> scenarios, ThemeResolver themeResolver) {
|
||||
this.terminal = terminal;
|
||||
this.themeResolver = themeResolver;
|
||||
mapScenarios(scenarios);
|
||||
}
|
||||
|
||||
@@ -117,7 +123,9 @@ public class Catalog {
|
||||
public void run() {
|
||||
ui = new TerminalUI(terminal);
|
||||
eventLoop = ui.getEventLoop();
|
||||
AppView app = buildScenarioBrowser(eventLoop, ui);
|
||||
app = buildScenarioBrowser(eventLoop, ui);
|
||||
app.setThemeResolver(themeResolver);
|
||||
app.setThemeName(activeThemeName);
|
||||
|
||||
// handle logic to switch between main scenario browser
|
||||
// and currently active scenario
|
||||
@@ -146,12 +154,14 @@ 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);
|
||||
grid.setRowSize(0);
|
||||
grid.setColumnSize(30, 0);
|
||||
|
||||
categories = buildCategorySelector(eventLoop);
|
||||
ListView<ScenarioData> scenarios = buildScenarioSelector(eventLoop);
|
||||
scenarios = buildScenarioSelector(eventLoop);
|
||||
|
||||
grid.addItem(categories, 0, 0, 1, 1, 0, 0);
|
||||
grid.addItem(scenarios, 0, 1, 1, 1, 0, 0);
|
||||
@@ -161,12 +171,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);
|
||||
|
||||
// handle event when scenario is chosen
|
||||
eventLoop.onDestroy(eventLoop.viewEvents(LISTVIEW_SCENARIO_TYPEREF, scenarios)
|
||||
.subscribe(event -> {
|
||||
View view = event.args().item().scenario().configure(eventLoop).build();
|
||||
View view = event.args().item().scenario()
|
||||
.configure(eventLoop, themeResolver, activeThemeName)
|
||||
.build();
|
||||
component.setRoot(view, true);
|
||||
currentScenarioView = view;
|
||||
}));
|
||||
@@ -212,6 +226,8 @@ public class Catalog {
|
||||
|
||||
private ListView<String> buildCategorySelector(EventLoop eventLoop) {
|
||||
ListView<String> categories = new ListView<>();
|
||||
categories.setThemeResolver(themeResolver);
|
||||
categories.setThemeName(activeThemeName);
|
||||
categories.setEventLoop(eventLoop);
|
||||
List<String> items = List.copyOf(categoryMap.keySet());
|
||||
categories.setItems(items);
|
||||
@@ -234,6 +250,8 @@ public class Catalog {
|
||||
|
||||
private ListView<ScenarioData> buildScenarioSelector(EventLoop eventLoop) {
|
||||
ListView<ScenarioData> scenarios = new ListView<>();
|
||||
scenarios.setThemeResolver(themeResolver);
|
||||
scenarios.setThemeName(activeThemeName);
|
||||
scenarios.setEventLoop(eventLoop);
|
||||
scenarios.setTitle("Scenarios");
|
||||
scenarios.setFocusedTitleStyle(ScreenItem.STYLE_BOLD);
|
||||
@@ -242,19 +260,36 @@ public class Catalog {
|
||||
return scenarios;
|
||||
}
|
||||
|
||||
private void setStyle(String name) {
|
||||
log.debug("Setting active theme name {}", name);
|
||||
activeThemeName = name;
|
||||
scenarios.setThemeName(activeThemeName);
|
||||
categories.setThemeName(activeThemeName);
|
||||
app.setThemeName(activeThemeName);
|
||||
}
|
||||
|
||||
private Runnable styleAction(String style) {
|
||||
return () -> setStyle(style);
|
||||
}
|
||||
|
||||
private MenuBarView buildMenuBar(EventLoop eventLoop) {
|
||||
Runnable quitAction = () -> requestQuit();
|
||||
MenuItem[] themeItems = themeResolver.themeNames().stream()
|
||||
.map(tn -> {
|
||||
return MenuItem.of(tn, MenuItemCheckStyle.RADIO, styleAction(tn), "default".equals(tn));
|
||||
})
|
||||
.toArray(MenuItem[]::new);
|
||||
MenuBarView menuBar = MenuBarView.of(
|
||||
MenuBarItem.of("File",
|
||||
MenuItem.of("Quit", MenuItemCheckStyle.NOCHECK, quitAction)),
|
||||
MenuBarItem.of("Theme",
|
||||
MenuItem.of("Dump", MenuItemCheckStyle.RADIO),
|
||||
MenuItem.of("Funky", MenuItemCheckStyle.RADIO)
|
||||
),
|
||||
themeItems),
|
||||
MenuBarItem.of("Help",
|
||||
MenuItem.of("About"))
|
||||
);
|
||||
|
||||
menuBar.setThemeResolver(themeResolver);
|
||||
menuBar.setThemeName(activeThemeName);
|
||||
menuBar.setEventLoop(eventLoop);
|
||||
return menuBar;
|
||||
}
|
||||
@@ -262,6 +297,8 @@ public class Catalog {
|
||||
private StatusBarView buildStatusBar(EventLoop eventLoop) {
|
||||
Runnable quitAction = () -> requestQuit();
|
||||
StatusBarView statusBar = new StatusBarView();
|
||||
statusBar.setThemeResolver(themeResolver);
|
||||
statusBar.setThemeName(activeThemeName);
|
||||
statusBar.setEventLoop(eventLoop);
|
||||
StatusItem item1 = new StatusBarView.StatusItem("CTRL-Q Quit", quitAction);
|
||||
StatusItem item2 = new StatusBarView.StatusItem("F10 Status Bar");
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
import org.springframework.shell.command.annotation.Command;
|
||||
import org.springframework.shell.samples.catalog.scenario.Scenario;
|
||||
import org.springframework.shell.standard.AbstractShellComponent;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
|
||||
/**
|
||||
* Main command access point to view showcase catalog.
|
||||
@@ -30,14 +31,16 @@ import org.springframework.shell.standard.AbstractShellComponent;
|
||||
public class CatalogCommand extends AbstractShellComponent {
|
||||
|
||||
private final List<Scenario> scenarios;
|
||||
private final ThemeResolver themeResolver;
|
||||
|
||||
public CatalogCommand(List<Scenario> scenarios) {
|
||||
public CatalogCommand(List<Scenario> scenarios, ThemeResolver themeResolver) {
|
||||
this.scenarios = scenarios;
|
||||
this.themeResolver = themeResolver;
|
||||
}
|
||||
|
||||
@Command(command = "catalog")
|
||||
public void catalog() {
|
||||
Catalog catalog = new Catalog(getTerminal(), scenarios);
|
||||
Catalog catalog = new Catalog(getTerminal(), scenarios, themeResolver);
|
||||
catalog.run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,50 @@
|
||||
*/
|
||||
package org.springframework.shell.samples.catalog;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.shell.command.annotation.CommandScan;
|
||||
import org.springframework.shell.style.FigureSettings;
|
||||
import org.springframework.shell.style.StyleSettings;
|
||||
import org.springframework.shell.style.Theme;
|
||||
import org.springframework.shell.style.ThemeSettings;
|
||||
|
||||
@Configuration
|
||||
@CommandScan
|
||||
class CatalogConfiguration {
|
||||
|
||||
@Bean
|
||||
public Theme customTheme() {
|
||||
return new Theme() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "custom";
|
||||
}
|
||||
@Override
|
||||
public ThemeSettings getSettings() {
|
||||
return new CustomThemeSettings();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static class CustomThemeSettings extends ThemeSettings {
|
||||
CustomThemeSettings() {
|
||||
super(new CustomStyleSettings(), FigureSettings.defaults());
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomStyleSettings extends StyleSettings {
|
||||
|
||||
@Override
|
||||
public String highlight() {
|
||||
return "bold,italic,fg:bright-cyan";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String background() {
|
||||
return "bg:blue";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
*/
|
||||
package org.springframework.shell.samples.catalog.scenario;
|
||||
|
||||
import org.springframework.shell.component.view.control.AbstractView;
|
||||
import org.springframework.shell.component.view.event.EventLoop;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
|
||||
/**
|
||||
* Base implementation of a {@link Scenario} helping to avoid some bloatware.
|
||||
@@ -25,10 +27,14 @@ import org.springframework.shell.component.view.event.EventLoop;
|
||||
public abstract class AbstractScenario implements Scenario {
|
||||
|
||||
private EventLoop eventloop;
|
||||
private ThemeResolver themeResolver;
|
||||
private String themeName;
|
||||
|
||||
@Override
|
||||
public Scenario configure(EventLoop eventloop) {
|
||||
public Scenario configure(EventLoop eventloop, ThemeResolver themeResolver, String themeName) {
|
||||
this.eventloop = eventloop;
|
||||
this.themeResolver = themeResolver;
|
||||
this.themeName = themeName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -36,4 +42,17 @@ public abstract class AbstractScenario implements Scenario {
|
||||
return eventloop;
|
||||
}
|
||||
|
||||
protected ThemeResolver getThemeResolver() {
|
||||
return themeResolver;
|
||||
}
|
||||
|
||||
protected String getThemeName() {
|
||||
return themeName;
|
||||
}
|
||||
|
||||
protected void configure(AbstractView view) {
|
||||
view.setEventLoop(getEventloop());
|
||||
view.setThemeResolver(getThemeResolver());
|
||||
view.setThemeName(getThemeName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.shell.samples.catalog.scenario;
|
||||
|
||||
import org.springframework.shell.component.view.control.View;
|
||||
import org.springframework.shell.component.view.event.EventLoop;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
|
||||
/**
|
||||
* {@link Scenario} participates in a catalog showcase.
|
||||
@@ -43,8 +44,10 @@ public interface Scenario {
|
||||
* Configure scenario.
|
||||
*
|
||||
* @param eventloop eventloop for scenario
|
||||
* @param themeResolver theme resolver for scenario
|
||||
* @param themeName theme name for scenario
|
||||
* @return scenario for chaining
|
||||
*/
|
||||
Scenario configure(EventLoop eventloop);
|
||||
Scenario configure(EventLoop eventloop, ThemeResolver themeResolver, String themeName);
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ public class SimpleBoxViewScenario extends AbstractScenario {
|
||||
@Override
|
||||
public View build() {
|
||||
BoxView box = new BoxView();
|
||||
configure(box);
|
||||
box.setTitle("Title");
|
||||
box.setShowBorder(true);
|
||||
box.setBackgroundColor(Color.KHAKI4);
|
||||
|
||||
@@ -29,31 +29,37 @@ public class SimpleGridViewScenario extends AbstractScenario {
|
||||
@Override
|
||||
public View build() {
|
||||
BoxView menu = new BoxView();
|
||||
configure(menu);
|
||||
menu.setBackgroundColor(Color.KHAKI4);
|
||||
menu.setTitle("Menu");
|
||||
menu.setShowBorder(true);
|
||||
|
||||
BoxView main = new BoxView();
|
||||
configure(main);
|
||||
main.setBackgroundColor(Color.KHAKI4);
|
||||
main.setTitle("Main");
|
||||
main.setShowBorder(true);
|
||||
|
||||
BoxView sideBar = new BoxView();
|
||||
configure(sideBar);
|
||||
sideBar.setBackgroundColor(Color.KHAKI4);
|
||||
sideBar.setTitle("Sidebar");
|
||||
sideBar.setShowBorder(true);
|
||||
|
||||
BoxView header = new BoxView();
|
||||
configure(header);
|
||||
header.setBackgroundColor(Color.KHAKI4);
|
||||
header.setTitle("Header");
|
||||
header.setShowBorder(true);
|
||||
|
||||
BoxView footer = new BoxView();
|
||||
configure(footer);
|
||||
footer.setBackgroundColor(Color.KHAKI4);
|
||||
footer.setTitle("Footer");
|
||||
footer.setShowBorder(true);
|
||||
|
||||
GridView grid = new GridView();
|
||||
configure(grid);
|
||||
grid.setBackgroundColor(Color.KHAKI3);
|
||||
grid.setTitle("Grid");
|
||||
grid.setShowBorder(true);
|
||||
|
||||
Reference in New Issue
Block a user