Skip to content

Commit ef71363

Browse files
committed
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 spring-projects#824
1 parent 5569273 commit ef71363

File tree

21 files changed

+480
-55
lines changed

21 files changed

+480
-55
lines changed

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

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@
2727

2828
import org.springframework.lang.Nullable;
2929
import org.springframework.messaging.Message;
30+
import org.springframework.shell.component.view.control.cell.AbstractControl;
3031
import org.springframework.shell.component.view.event.EventLoop;
3132
import org.springframework.shell.component.view.event.KeyBindingConsumer;
3233
import org.springframework.shell.component.view.event.KeyBindingConsumerArgs;
3334
import org.springframework.shell.component.view.event.KeyEvent;
3435
import org.springframework.shell.component.view.event.KeyHandler;
35-
import org.springframework.shell.component.view.event.MouseBindingConsumerArgs;
3636
import org.springframework.shell.component.view.event.MouseBindingConsumer;
37+
import org.springframework.shell.component.view.event.MouseBindingConsumerArgs;
3738
import org.springframework.shell.component.view.event.MouseEvent;
3839
import org.springframework.shell.component.view.event.MouseHandler;
3940
import org.springframework.shell.component.view.geom.Rectangle;
@@ -46,14 +47,10 @@
4647
*
4748
* @author Janne Valkealahti
4849
*/
49-
public abstract class AbstractView implements View {
50+
public abstract class AbstractView extends AbstractControl implements View {
5051

5152
private final static Logger log = LoggerFactory.getLogger(AbstractView.class);
5253
private final Disposable.Composite disposables = Disposables.composite();
53-
private int x = 0;
54-
private int y = 0;
55-
private int width = 0;
56-
private int height = 0;
5754
private BiFunction<Screen, Rectangle, Rectangle> drawFunction;
5855
private boolean hasFocus;
5956
private int layer;
@@ -99,19 +96,6 @@ protected final void init() {
9996
protected void initInternal() {
10097
}
10198

102-
@Override
103-
public void setRect(int x, int y, int width, int height) {
104-
this.x = x;
105-
this.y = y;
106-
this.width = width;
107-
this.height = height;
108-
}
109-
110-
@Override
111-
public Rectangle getRect() {
112-
return new Rectangle(x, y, width, height);
113-
}
114-
11599
@Override
116100
public void setLayer(int index) {
117101
this.layer = index;

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.springframework.shell.component.view.geom.Rectangle;
2323
import org.springframework.shell.component.view.message.ShellMessageBuilder;
2424
import org.springframework.shell.component.view.screen.Screen;
25+
import org.springframework.shell.style.ThemeResolver;
2526
import org.springframework.util.Assert;
2627

2728
/**
@@ -49,6 +50,22 @@ public AppView(View main, View menuBar, View statusBar) {
4950
initLayout();
5051
}
5152

53+
@Override
54+
public void setThemeName(String themeName) {
55+
super.setThemeName(themeName);
56+
main.setThemeName(themeName);
57+
menu.setThemeName(themeName);
58+
status.setThemeName(themeName);
59+
}
60+
61+
@Override
62+
public void setThemeResolver(ThemeResolver themeResolver) {
63+
super.setThemeResolver(themeResolver);
64+
main.setThemeResolver(themeResolver);
65+
menu.setThemeResolver(themeResolver);
66+
status.setThemeResolver(themeResolver);
67+
}
68+
5269
private void initLayout() {
5370
grid = new GridView();
5471
grid.setRowSize(1, 0, 1);

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

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
import org.slf4j.Logger;
1919
import org.slf4j.LoggerFactory;
2020

21-
import org.springframework.lang.Nullable;
2221
import org.springframework.shell.component.view.geom.HorizontalAlign;
2322
import org.springframework.shell.component.view.geom.Rectangle;
2423
import org.springframework.shell.component.view.geom.VerticalAlign;
2524
import org.springframework.shell.component.view.screen.Screen;
25+
import org.springframework.shell.style.StyleSettings;
2626
import org.springframework.util.StringUtils;
2727

2828
/**
@@ -45,7 +45,8 @@ public class BoxView extends AbstractView {
4545
private int paddingBottom;
4646
private int paddingLeft;
4747
private int paddingRight;
48-
private Integer backgroundColor = -1;
48+
private boolean transparent = true;
49+
private int backgroundColor = -1;
4950
private int titleColor = -1;
5051
private int titleStyle = -1;
5152
private int focusedTitleColor = -1;
@@ -110,7 +111,7 @@ public void setTitle(String title) {
110111
*
111112
* @param backgroundColor the background color
112113
*/
113-
public void setBackgroundColor(@Nullable Integer backgroundColor) {
114+
public void setBackgroundColor(int backgroundColor) {
114115
this.backgroundColor = backgroundColor;
115116
}
116117

@@ -161,6 +162,24 @@ public void setTitleAlign(HorizontalAlign titleAlign) {
161162
this.titleAlign = titleAlign;
162163
}
163164

165+
/**
166+
* Sets if box should be transparent, {@code true} by default.
167+
*
168+
* @param transparent a transparency flag
169+
*/
170+
public void setTransparent(boolean transparent) {
171+
this.transparent = transparent;
172+
}
173+
174+
/**
175+
* Is box transparent.
176+
*
177+
* @return box transparency
178+
*/
179+
protected boolean isTransparent() {
180+
return transparent;
181+
}
182+
164183
/**
165184
* Possibly draws a box around this view and title in a box top boundary. Also
166185
* calls a {@code draw function} if defined.
@@ -173,12 +192,14 @@ protected void drawInternal(Screen screen) {
173192
if (rect.width() <= 0 || rect.height() <= 0) {
174193
return;
175194
}
176-
if (backgroundColor == null) {
177-
screen.writerBuilder().layer(getLayer()).build().background(rect, -1);
195+
int bgColor;
196+
if (isTransparent()) {
197+
bgColor = backgroundColor > -1 ? backgroundColor : -1;
178198
}
179-
else if (backgroundColor > -1) {
180-
screen.writerBuilder().layer(getLayer()).build().background(rect, backgroundColor);
199+
else {
200+
bgColor = resolveThemeBackground(StyleSettings.TAG_BACKGROUND, backgroundColor, -1);
181201
}
202+
screen.writerBuilder().layer(getLayer()).build().background(rect, bgColor);
182203
if (showBorder && rect.width() >= 2 && rect.height() >= 2) {
183204
screen.writerBuilder().layer(getLayer()).build().border(rect.x(), rect.y(), rect.width(), rect.height());
184205
if (StringUtils.hasText(title)) {

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
*/
1616
package org.springframework.shell.component.view.control;
1717

18+
import org.springframework.lang.Nullable;
1819
import org.springframework.shell.component.view.geom.Rectangle;
1920
import org.springframework.shell.component.view.screen.Screen;
21+
import org.springframework.shell.style.ThemeResolver;
2022

2123
/**
2224
* Base interface for all controls. {@link Control} is able to define a
@@ -51,4 +53,19 @@ public interface Control {
5153
* @param height a height of a bounded box
5254
*/
5355
void setRect(int x, int y, int width, int height);
56+
57+
/**
58+
* Sets a {@link ThemeResolver}.
59+
*
60+
* @param themeResolver the theme resolver
61+
*/
62+
void setThemeResolver(@Nullable ThemeResolver themeResolver);
63+
64+
/**
65+
* Sets a theme name to use.
66+
*
67+
* @param themeName the theme name
68+
*/
69+
void setThemeName(@Nullable String themeName);
70+
5471
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.shell.component.view.message.ShellMessageBuilder;
2727
import org.springframework.shell.component.view.screen.Screen;
2828
import org.springframework.shell.component.view.screen.ScreenItem;
29+
import org.springframework.shell.style.StyleSettings;
2930

3031
/**
3132
* {@link ListView} shows {@code list items} vertically.
@@ -51,12 +52,13 @@ protected void drawInternal(Screen screen) {
5152
Rectangle rect = getInnerRect();
5253
int y = rect.y();
5354

55+
int selectedStyle = resolveThemeStyle(StyleSettings.TAG_HIGHLIGHT, ScreenItem.STYLE_BOLD);
5456
int i = 0;
5557
for (ListCell<T> c : cells) {
5658
c.setRect(rect.x(), y++, rect.width(), 1);
5759
if (i == selected) {
5860
c.updateSelected(true);
59-
c.setStyle(ScreenItem.STYLE_BOLD);
61+
c.setStyle(selectedStyle);
6062
}
6163
else {
6264
c.updateSelected(false);

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.shell.component.view.geom.Rectangle;
3636
import org.springframework.shell.component.view.screen.Screen;
3737
import org.springframework.shell.component.view.screen.Screen.Writer;
38+
import org.springframework.shell.style.ThemeResolver;
3839
import org.springframework.shell.component.view.screen.ScreenItem;
3940

4041
/**
@@ -201,6 +202,18 @@ private void select(MouseEvent event) {
201202
checkMenuView();
202203
}
203204

205+
@Override
206+
public void setThemeName(String themeName) {
207+
super.setThemeName(themeName);
208+
menuViews.values().forEach(view -> view.setThemeName(themeName));
209+
}
210+
211+
@Override
212+
public void setThemeResolver(ThemeResolver themeResolver) {
213+
super.setThemeResolver(themeResolver);
214+
menuViews.values().forEach(view -> view.setThemeResolver(themeResolver));
215+
}
216+
204217
private void checkMenuView() {
205218
if (activeItemIndex < 0) {
206219
closeCurrentMenuView();
@@ -218,8 +231,10 @@ private void closeCurrentMenuView() {
218231
private MenuView buildMenuView(MenuBarItem item) {
219232
MenuView menuView = new MenuView(item.getItems());
220233
menuView.setEventLoop(getEventLoop());
234+
menuView.setThemeResolver(getThemeResolver());
235+
menuView.setThemeName(getThemeName());
221236
menuView.setShowBorder(true);
222-
menuView.setBackgroundColor(null);
237+
menuView.setTransparent(false);
223238
menuView.setLayer(1);
224239
Rectangle rect = getInnerRect();
225240
int x = positionAtIndex(activeItemIndex);

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.shell.component.view.message.ShellMessageBuilder;
3434
import org.springframework.shell.component.view.screen.Screen;
3535
import org.springframework.shell.component.view.screen.Screen.Writer;
36+
import org.springframework.shell.style.StyleSettings;
3637
import org.springframework.shell.component.view.screen.ScreenItem;
3738
import org.springframework.util.Assert;
3839
import org.springframework.util.StringUtils;
@@ -143,7 +144,8 @@ protected void drawInternal(Screen screen) {
143144
Rectangle rect = getInnerRect();
144145
int y = rect.y();
145146
Writer writer = screen.writerBuilder().layer(getLayer()).build();
146-
Writer writer2 = screen.writerBuilder().layer(getLayer()).style(ScreenItem.STYLE_BOLD).build();
147+
int themeStyle = resolveThemeStyle(StyleSettings.TAG_HIGHLIGHT, ScreenItem.STYLE_BOLD);
148+
Writer writer2 = screen.writerBuilder().layer(getLayer()).style(themeStyle).build();
147149
int i = 0;
148150
boolean hasCheck = false;
149151
for (MenuItem item : items) {

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

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,15 @@
1515
*/
1616
package org.springframework.shell.component.view.control.cell;
1717

18+
import java.util.Optional;
19+
20+
import org.jline.utils.AttributedStyle;
21+
22+
import org.springframework.lang.Nullable;
1823
import org.springframework.shell.component.view.control.Control;
1924
import org.springframework.shell.component.view.geom.Rectangle;
25+
import org.springframework.shell.style.ThemeResolver;
26+
import org.springframework.shell.style.ThemeResolver.ResolvedValues;
2027

2128
/**
2229
* Base implementation of a {@link Control}.
@@ -29,6 +36,8 @@ public abstract class AbstractControl implements Control {
2936
private int y = 0;
3037
private int width = 0;
3138
private int height = 0;
39+
private ThemeResolver themeResolver;
40+
private String themeName;
3241

3342
@Override
3443
public void setRect(int x, int y, int width, int height) {
@@ -43,4 +52,98 @@ public Rectangle getRect() {
4352
return new Rectangle(x, y, width, height);
4453
}
4554

55+
/**
56+
* Sets a {@link ThemeResolver}.
57+
*
58+
* @param themeResolver the theme resolver
59+
*/
60+
public void setThemeResolver(@Nullable ThemeResolver themeResolver) {
61+
this.themeResolver = themeResolver;
62+
}
63+
64+
/**
65+
* Gets a {@link ThemeResolver}.
66+
*
67+
* @return a theme resolver
68+
*/
69+
@Nullable
70+
protected ThemeResolver getThemeResolver() {
71+
return themeResolver;
72+
}
73+
74+
/**
75+
* Sets a theme name to use.
76+
*
77+
* @param themeName the theme name
78+
*/
79+
public void setThemeName(@Nullable String themeName) {
80+
this.themeName = themeName;
81+
}
82+
83+
/**
84+
* Gets a theme name.
85+
*
86+
* @return a theme name
87+
*/
88+
@Nullable
89+
protected String getThemeName() {
90+
return themeName;
91+
}
92+
93+
private Optional<ResolvedValues> getThemeResolvedValues(String tag) {
94+
ThemeResolver themeResolver = getThemeResolver();
95+
if (themeResolver != null) {
96+
String styleTag = themeResolver.resolveStyleTag(tag, getThemeName());
97+
AttributedStyle attributedStyle = themeResolver.resolveStyle(styleTag);
98+
return Optional.of(themeResolver.resolveValues(attributedStyle));
99+
}
100+
return Optional.empty();
101+
}
102+
103+
/**
104+
* Resolve style using existing {@link ThemeResolver} and {@code theme name}.
105+
* Use {@code defaultStyle} if resolving cannot happen.
106+
*
107+
* @param tag the style tag to use
108+
* @param defaultStyle the default style to use
109+
* @return resolved style
110+
*/
111+
protected int resolveThemeStyle(String tag, int defaultStyle) {
112+
return getThemeResolvedValues(tag).map(ResolvedValues::style).orElse(defaultStyle);
113+
}
114+
115+
/**
116+
* Resolve foreground color using existing {@link ThemeResolver} and {@code theme name}.
117+
* {@code defaultColor} is used if it's value is not negative. {@code fallbackColor} is
118+
* used if theme resolver cannot be used.
119+
*
120+
* @param tag the style tag to use
121+
* @param defaultColor the default foreground color to use
122+
* @param fallbackColor the fallback foreground color to use
123+
* @return resolved foreground color
124+
*/
125+
protected int resolveThemeForeground(String tag, int defaultColor, int fallbackColor) {
126+
if (defaultColor > -1) {
127+
return defaultColor;
128+
}
129+
return getThemeResolvedValues(tag).map(ResolvedValues::foreground).orElse(fallbackColor);
130+
}
131+
132+
/**
133+
* Resolve background color using existing {@link ThemeResolver} and {@code theme name}.
134+
* {@code defaultColor} is used if it's value is not negative. {@code fallbackColor} is
135+
* used if theme resolver cannot be used.
136+
*
137+
* @param tag the style tag to use
138+
* @param defaultColor the default background color to use
139+
* @param fallbackColor the fallback background color to use
140+
* @return resolved background color
141+
*/
142+
protected int resolveThemeBackground(String tag, int defaultColor, int fallbackColor) {
143+
if (defaultColor > -1) {
144+
return defaultColor;
145+
}
146+
return getThemeResolvedValues(tag).map(ResolvedValues::background).orElse(fallbackColor);
147+
}
148+
46149
}

0 commit comments

Comments
 (0)