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:
@@ -27,13 +27,14 @@ import reactor.core.Disposables;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.shell.component.view.control.cell.AbstractControl;
|
||||
import org.springframework.shell.component.view.event.EventLoop;
|
||||
import org.springframework.shell.component.view.event.KeyBindingConsumer;
|
||||
import org.springframework.shell.component.view.event.KeyBindingConsumerArgs;
|
||||
import org.springframework.shell.component.view.event.KeyEvent;
|
||||
import org.springframework.shell.component.view.event.KeyHandler;
|
||||
import org.springframework.shell.component.view.event.MouseBindingConsumerArgs;
|
||||
import org.springframework.shell.component.view.event.MouseBindingConsumer;
|
||||
import org.springframework.shell.component.view.event.MouseBindingConsumerArgs;
|
||||
import org.springframework.shell.component.view.event.MouseEvent;
|
||||
import org.springframework.shell.component.view.event.MouseHandler;
|
||||
import org.springframework.shell.component.view.geom.Rectangle;
|
||||
@@ -46,14 +47,10 @@ import org.springframework.shell.component.view.screen.Screen;
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public abstract class AbstractView implements View {
|
||||
public abstract class AbstractView extends AbstractControl implements View {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(AbstractView.class);
|
||||
private final Disposable.Composite disposables = Disposables.composite();
|
||||
private int x = 0;
|
||||
private int y = 0;
|
||||
private int width = 0;
|
||||
private int height = 0;
|
||||
private BiFunction<Screen, Rectangle, Rectangle> drawFunction;
|
||||
private boolean hasFocus;
|
||||
private int layer;
|
||||
@@ -99,19 +96,6 @@ public abstract class AbstractView implements View {
|
||||
protected void initInternal() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRect(int x, int y, int width, int height) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getRect() {
|
||||
return new Rectangle(x, y, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLayer(int index) {
|
||||
this.layer = index;
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.shell.component.view.event.MouseHandler;
|
||||
import org.springframework.shell.component.view.geom.Rectangle;
|
||||
import org.springframework.shell.component.view.message.ShellMessageBuilder;
|
||||
import org.springframework.shell.component.view.screen.Screen;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -49,6 +50,22 @@ public class AppView extends BoxView {
|
||||
initLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThemeName(String themeName) {
|
||||
super.setThemeName(themeName);
|
||||
main.setThemeName(themeName);
|
||||
menu.setThemeName(themeName);
|
||||
status.setThemeName(themeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThemeResolver(ThemeResolver themeResolver) {
|
||||
super.setThemeResolver(themeResolver);
|
||||
main.setThemeResolver(themeResolver);
|
||||
menu.setThemeResolver(themeResolver);
|
||||
status.setThemeResolver(themeResolver);
|
||||
}
|
||||
|
||||
private void initLayout() {
|
||||
grid = new GridView();
|
||||
grid.setRowSize(1, 0, 1);
|
||||
|
||||
@@ -18,11 +18,11 @@ package org.springframework.shell.component.view.control;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.shell.component.view.geom.HorizontalAlign;
|
||||
import org.springframework.shell.component.view.geom.Rectangle;
|
||||
import org.springframework.shell.component.view.geom.VerticalAlign;
|
||||
import org.springframework.shell.component.view.screen.Screen;
|
||||
import org.springframework.shell.style.StyleSettings;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -45,7 +45,8 @@ public class BoxView extends AbstractView {
|
||||
private int paddingBottom;
|
||||
private int paddingLeft;
|
||||
private int paddingRight;
|
||||
private Integer backgroundColor = -1;
|
||||
private boolean transparent = true;
|
||||
private int backgroundColor = -1;
|
||||
private int titleColor = -1;
|
||||
private int titleStyle = -1;
|
||||
private int focusedTitleColor = -1;
|
||||
@@ -110,7 +111,7 @@ public class BoxView extends AbstractView {
|
||||
*
|
||||
* @param backgroundColor the background color
|
||||
*/
|
||||
public void setBackgroundColor(@Nullable Integer backgroundColor) {
|
||||
public void setBackgroundColor(int backgroundColor) {
|
||||
this.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
@@ -161,6 +162,24 @@ public class BoxView extends AbstractView {
|
||||
this.titleAlign = titleAlign;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if box should be transparent, {@code true} by default.
|
||||
*
|
||||
* @param transparent a transparency flag
|
||||
*/
|
||||
public void setTransparent(boolean transparent) {
|
||||
this.transparent = transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is box transparent.
|
||||
*
|
||||
* @return box transparency
|
||||
*/
|
||||
protected boolean isTransparent() {
|
||||
return transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly draws a box around this view and title in a box top boundary. Also
|
||||
* calls a {@code draw function} if defined.
|
||||
@@ -173,12 +192,14 @@ public class BoxView extends AbstractView {
|
||||
if (rect.width() <= 0 || rect.height() <= 0) {
|
||||
return;
|
||||
}
|
||||
if (backgroundColor == null) {
|
||||
screen.writerBuilder().layer(getLayer()).build().background(rect, -1);
|
||||
int bgColor;
|
||||
if (isTransparent()) {
|
||||
bgColor = backgroundColor > -1 ? backgroundColor : -1;
|
||||
}
|
||||
else if (backgroundColor > -1) {
|
||||
screen.writerBuilder().layer(getLayer()).build().background(rect, backgroundColor);
|
||||
else {
|
||||
bgColor = resolveThemeBackground(StyleSettings.TAG_BACKGROUND, backgroundColor, -1);
|
||||
}
|
||||
screen.writerBuilder().layer(getLayer()).build().background(rect, bgColor);
|
||||
if (showBorder && rect.width() >= 2 && rect.height() >= 2) {
|
||||
screen.writerBuilder().layer(getLayer()).build().border(rect.x(), rect.y(), rect.width(), rect.height());
|
||||
if (StringUtils.hasText(title)) {
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
*/
|
||||
package org.springframework.shell.component.view.control;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.shell.component.view.geom.Rectangle;
|
||||
import org.springframework.shell.component.view.screen.Screen;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
|
||||
/**
|
||||
* Base interface for all controls. {@link Control} is able to define a
|
||||
@@ -51,4 +53,19 @@ public interface Control {
|
||||
* @param height a height of a bounded box
|
||||
*/
|
||||
void setRect(int x, int y, int width, int height);
|
||||
|
||||
/**
|
||||
* Sets a {@link ThemeResolver}.
|
||||
*
|
||||
* @param themeResolver the theme resolver
|
||||
*/
|
||||
void setThemeResolver(@Nullable ThemeResolver themeResolver);
|
||||
|
||||
/**
|
||||
* Sets a theme name to use.
|
||||
*
|
||||
* @param themeName the theme name
|
||||
*/
|
||||
void setThemeName(@Nullable String themeName);
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.shell.component.view.geom.Rectangle;
|
||||
import org.springframework.shell.component.view.message.ShellMessageBuilder;
|
||||
import org.springframework.shell.component.view.screen.Screen;
|
||||
import org.springframework.shell.component.view.screen.ScreenItem;
|
||||
import org.springframework.shell.style.StyleSettings;
|
||||
|
||||
/**
|
||||
* {@link ListView} shows {@code list items} vertically.
|
||||
@@ -51,12 +52,13 @@ public class ListView<T> extends BoxView {
|
||||
Rectangle rect = getInnerRect();
|
||||
int y = rect.y();
|
||||
|
||||
int selectedStyle = resolveThemeStyle(StyleSettings.TAG_HIGHLIGHT, ScreenItem.STYLE_BOLD);
|
||||
int i = 0;
|
||||
for (ListCell<T> c : cells) {
|
||||
c.setRect(rect.x(), y++, rect.width(), 1);
|
||||
if (i == selected) {
|
||||
c.updateSelected(true);
|
||||
c.setStyle(ScreenItem.STYLE_BOLD);
|
||||
c.setStyle(selectedStyle);
|
||||
}
|
||||
else {
|
||||
c.updateSelected(false);
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.shell.component.view.geom.Dimension;
|
||||
import org.springframework.shell.component.view.geom.Rectangle;
|
||||
import org.springframework.shell.component.view.screen.Screen;
|
||||
import org.springframework.shell.component.view.screen.Screen.Writer;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
import org.springframework.shell.component.view.screen.ScreenItem;
|
||||
|
||||
/**
|
||||
@@ -201,6 +202,18 @@ public class MenuBarView extends BoxView {
|
||||
checkMenuView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThemeName(String themeName) {
|
||||
super.setThemeName(themeName);
|
||||
menuViews.values().forEach(view -> view.setThemeName(themeName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThemeResolver(ThemeResolver themeResolver) {
|
||||
super.setThemeResolver(themeResolver);
|
||||
menuViews.values().forEach(view -> view.setThemeResolver(themeResolver));
|
||||
}
|
||||
|
||||
private void checkMenuView() {
|
||||
if (activeItemIndex < 0) {
|
||||
closeCurrentMenuView();
|
||||
@@ -218,8 +231,10 @@ public class MenuBarView extends BoxView {
|
||||
private MenuView buildMenuView(MenuBarItem item) {
|
||||
MenuView menuView = new MenuView(item.getItems());
|
||||
menuView.setEventLoop(getEventLoop());
|
||||
menuView.setThemeResolver(getThemeResolver());
|
||||
menuView.setThemeName(getThemeName());
|
||||
menuView.setShowBorder(true);
|
||||
menuView.setBackgroundColor(null);
|
||||
menuView.setTransparent(false);
|
||||
menuView.setLayer(1);
|
||||
Rectangle rect = getInnerRect();
|
||||
int x = positionAtIndex(activeItemIndex);
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.shell.component.view.geom.Rectangle;
|
||||
import org.springframework.shell.component.view.message.ShellMessageBuilder;
|
||||
import org.springframework.shell.component.view.screen.Screen;
|
||||
import org.springframework.shell.component.view.screen.Screen.Writer;
|
||||
import org.springframework.shell.style.StyleSettings;
|
||||
import org.springframework.shell.component.view.screen.ScreenItem;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -143,7 +144,8 @@ public class MenuView extends BoxView {
|
||||
Rectangle rect = getInnerRect();
|
||||
int y = rect.y();
|
||||
Writer writer = screen.writerBuilder().layer(getLayer()).build();
|
||||
Writer writer2 = screen.writerBuilder().layer(getLayer()).style(ScreenItem.STYLE_BOLD).build();
|
||||
int themeStyle = resolveThemeStyle(StyleSettings.TAG_HIGHLIGHT, ScreenItem.STYLE_BOLD);
|
||||
Writer writer2 = screen.writerBuilder().layer(getLayer()).style(themeStyle).build();
|
||||
int i = 0;
|
||||
boolean hasCheck = false;
|
||||
for (MenuItem item : items) {
|
||||
|
||||
@@ -15,8 +15,15 @@
|
||||
*/
|
||||
package org.springframework.shell.component.view.control.cell;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.jline.utils.AttributedStyle;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.shell.component.view.control.Control;
|
||||
import org.springframework.shell.component.view.geom.Rectangle;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
import org.springframework.shell.style.ThemeResolver.ResolvedValues;
|
||||
|
||||
/**
|
||||
* Base implementation of a {@link Control}.
|
||||
@@ -29,6 +36,8 @@ public abstract class AbstractControl implements Control {
|
||||
private int y = 0;
|
||||
private int width = 0;
|
||||
private int height = 0;
|
||||
private ThemeResolver themeResolver;
|
||||
private String themeName;
|
||||
|
||||
@Override
|
||||
public void setRect(int x, int y, int width, int height) {
|
||||
@@ -43,4 +52,98 @@ public abstract class AbstractControl implements Control {
|
||||
return new Rectangle(x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link ThemeResolver}.
|
||||
*
|
||||
* @param themeResolver the theme resolver
|
||||
*/
|
||||
public void setThemeResolver(@Nullable ThemeResolver themeResolver) {
|
||||
this.themeResolver = themeResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link ThemeResolver}.
|
||||
*
|
||||
* @return a theme resolver
|
||||
*/
|
||||
@Nullable
|
||||
protected ThemeResolver getThemeResolver() {
|
||||
return themeResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a theme name to use.
|
||||
*
|
||||
* @param themeName the theme name
|
||||
*/
|
||||
public void setThemeName(@Nullable String themeName) {
|
||||
this.themeName = themeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a theme name.
|
||||
*
|
||||
* @return a theme name
|
||||
*/
|
||||
@Nullable
|
||||
protected String getThemeName() {
|
||||
return themeName;
|
||||
}
|
||||
|
||||
private Optional<ResolvedValues> getThemeResolvedValues(String tag) {
|
||||
ThemeResolver themeResolver = getThemeResolver();
|
||||
if (themeResolver != null) {
|
||||
String styleTag = themeResolver.resolveStyleTag(tag, getThemeName());
|
||||
AttributedStyle attributedStyle = themeResolver.resolveStyle(styleTag);
|
||||
return Optional.of(themeResolver.resolveValues(attributedStyle));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve style using existing {@link ThemeResolver} and {@code theme name}.
|
||||
* Use {@code defaultStyle} if resolving cannot happen.
|
||||
*
|
||||
* @param tag the style tag to use
|
||||
* @param defaultStyle the default style to use
|
||||
* @return resolved style
|
||||
*/
|
||||
protected int resolveThemeStyle(String tag, int defaultStyle) {
|
||||
return getThemeResolvedValues(tag).map(ResolvedValues::style).orElse(defaultStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve foreground color using existing {@link ThemeResolver} and {@code theme name}.
|
||||
* {@code defaultColor} is used if it's value is not negative. {@code fallbackColor} is
|
||||
* used if theme resolver cannot be used.
|
||||
*
|
||||
* @param tag the style tag to use
|
||||
* @param defaultColor the default foreground color to use
|
||||
* @param fallbackColor the fallback foreground color to use
|
||||
* @return resolved foreground color
|
||||
*/
|
||||
protected int resolveThemeForeground(String tag, int defaultColor, int fallbackColor) {
|
||||
if (defaultColor > -1) {
|
||||
return defaultColor;
|
||||
}
|
||||
return getThemeResolvedValues(tag).map(ResolvedValues::foreground).orElse(fallbackColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve background color using existing {@link ThemeResolver} and {@code theme name}.
|
||||
* {@code defaultColor} is used if it's value is not negative. {@code fallbackColor} is
|
||||
* used if theme resolver cannot be used.
|
||||
*
|
||||
* @param tag the style tag to use
|
||||
* @param defaultColor the default background color to use
|
||||
* @param fallbackColor the fallback background color to use
|
||||
* @return resolved background color
|
||||
*/
|
||||
protected int resolveThemeBackground(String tag, int defaultColor, int fallbackColor) {
|
||||
if (defaultColor > -1) {
|
||||
return defaultColor;
|
||||
}
|
||||
return getThemeResolvedValues(tag).map(ResolvedValues::background).orElse(fallbackColor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -257,10 +257,15 @@ public class DefaultScreen implements Screen, DisplayLines {
|
||||
// if (items[y] == null) {
|
||||
// items[y] = new DefaultScreenItem[columns];
|
||||
// }
|
||||
if (items[y][x] == null) {
|
||||
items[y][x] = new DefaultScreenItem();
|
||||
if (y < rows && x < columns) {
|
||||
if (items[y][x] == null) {
|
||||
items[y][x] = new DefaultScreenItem();
|
||||
}
|
||||
return items[y][x];
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
return items[y][x];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,7 +348,9 @@ public class DefaultScreen implements Screen, DisplayLines {
|
||||
for (int i = rect.y(); i < rect.y() + rect.height(); i++) {
|
||||
for (int j = rect.x(); j < rect.x() + rect.width(); j++) {
|
||||
DefaultScreenItem item = layer.getScreenItem(j, i);
|
||||
item.background = color;
|
||||
if (item != null) {
|
||||
item.background = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-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.
|
||||
@@ -91,6 +91,11 @@ public abstract class StyleSettings {
|
||||
*/
|
||||
public final static String TAG_HIGHLIGHT = "style-highlight";
|
||||
|
||||
/**
|
||||
* Styling for something which has a background.
|
||||
*/
|
||||
public final static String TAG_BACKGROUND = "style-background";
|
||||
|
||||
public String title() {
|
||||
return "bold";
|
||||
}
|
||||
@@ -143,6 +148,10 @@ public abstract class StyleSettings {
|
||||
return "bold";
|
||||
}
|
||||
|
||||
public String background() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a theme setting from a given tag.
|
||||
*
|
||||
@@ -177,6 +186,8 @@ public abstract class StyleSettings {
|
||||
return itemSelector();
|
||||
case TAG_HIGHLIGHT:
|
||||
return highlight();
|
||||
case TAG_BACKGROUND:
|
||||
return background();
|
||||
}
|
||||
throw new IllegalArgumentException(String.format("Unknown tag '%s'", tag));
|
||||
}
|
||||
@@ -213,7 +224,8 @@ public abstract class StyleSettings {
|
||||
TAG_ITEM_SELECTED,
|
||||
TAG_ITEM_UNSELECTED,
|
||||
TAG_ITEM_SELECTOR,
|
||||
TAG_HIGHLIGHT
|
||||
TAG_HIGHLIGHT,
|
||||
TAG_BACKGROUND
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-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.
|
||||
@@ -17,6 +17,7 @@ package org.springframework.shell.style;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -48,4 +49,13 @@ public class ThemeRegistry {
|
||||
Assert.notNull(theme, "theme cannot be null");
|
||||
themes.put(theme.getName(), theme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all theme names registered.
|
||||
*
|
||||
* @return theme names
|
||||
*/
|
||||
public Set<String> getThemeNames() {
|
||||
return themes.keySet();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-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.
|
||||
@@ -15,12 +15,17 @@
|
||||
*/
|
||||
package org.springframework.shell.style;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.jline.style.MemoryStyleSource;
|
||||
import org.jline.style.StyleExpression;
|
||||
import org.jline.style.StyleResolver;
|
||||
import org.jline.style.StyleSource;
|
||||
import org.jline.utils.AttributedString;
|
||||
import org.jline.utils.AttributedStyle;
|
||||
import org.jline.utils.Colors;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Service which helps to do various things with styles.
|
||||
@@ -32,12 +37,67 @@ public class ThemeResolver {
|
||||
private StyleSource styleSource = new MemoryStyleSource();
|
||||
private StyleResolver styleResolver = new StyleResolver(styleSource, "default");
|
||||
private StyleExpression styleExpression = new StyleExpression(styleResolver);
|
||||
private ThemeRegistry themeRegistry;
|
||||
private final Theme theme;
|
||||
|
||||
public ThemeResolver(ThemeRegistry themeRegistry, String themeName) {
|
||||
this.themeRegistry = themeRegistry;
|
||||
this.theme = themeRegistry.get(themeName);
|
||||
}
|
||||
|
||||
// copied from jline as no public access
|
||||
private static final long F_FOREGROUND_IND = 0x00000100;
|
||||
private static final long F_FOREGROUND_RGB = 0x00000200;
|
||||
private static final long F_FOREGROUND = F_FOREGROUND_IND | F_FOREGROUND_RGB;
|
||||
private static final long F_BACKGROUND_IND = 0x00000400;
|
||||
private static final long F_BACKGROUND_RGB = 0x00000800;
|
||||
private static final long F_BACKGROUND = F_BACKGROUND_IND | F_BACKGROUND_RGB;
|
||||
private static final int FG_COLOR_EXP = 15;
|
||||
private static final int BG_COLOR_EXP = 39;
|
||||
private static final long FG_COLOR = 0xFFFFFFL << FG_COLOR_EXP;
|
||||
private static final long BG_COLOR = 0xFFFFFFL << BG_COLOR_EXP;
|
||||
|
||||
/**
|
||||
* Stores resolved values for {@link ThemeResolver#resolveValues(AttributedStyle)}.
|
||||
*/
|
||||
public record ResolvedValues(int style, int foreground, int background){}
|
||||
|
||||
/**
|
||||
* Resolve {@code style}, {@code foreground color} and {@code background color}
|
||||
* from an {@link AttributedStyle}.
|
||||
*
|
||||
* @param attributedStyle the attibuted style
|
||||
* @return resolved values
|
||||
*/
|
||||
public ResolvedValues resolveValues(AttributedStyle attributedStyle) {
|
||||
long style = attributedStyle.getStyle();
|
||||
long s = style & ~(F_FOREGROUND | F_BACKGROUND);
|
||||
s = (s & 0x00007FFF);
|
||||
int fg = (int) ((style & FG_COLOR) >> FG_COLOR_EXP);
|
||||
int bg = (int) ((style & BG_COLOR) >> BG_COLOR_EXP);
|
||||
// if jline "ind" bit is set, resort to using
|
||||
// same logic as jline.
|
||||
boolean hasFb = (style & F_FOREGROUND_IND) != 0 || (style & F_FOREGROUND_RGB) != 0;
|
||||
if ((style & F_FOREGROUND_IND) != 0) {
|
||||
fg = Colors.DEFAULT_COLORS_256[fg & 0xFF];
|
||||
}
|
||||
boolean hasBg = (style & F_BACKGROUND_IND) != 0 || (style & F_BACKGROUND_RGB) != 0;
|
||||
if ((style & F_BACKGROUND_IND) != 0) {
|
||||
bg = Colors.DEFAULT_COLORS_256[bg & 0xFF];
|
||||
}
|
||||
|
||||
return new ResolvedValues((int)s, hasFb ? fg : -1, hasBg ? bg : -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all known theme names.
|
||||
*
|
||||
* @return all theme names
|
||||
*/
|
||||
public Set<String> themeNames() {
|
||||
return themeRegistry.getThemeNames();
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate expression.
|
||||
*
|
||||
@@ -48,6 +108,7 @@ public class ThemeResolver {
|
||||
return styleExpression.evaluate(expression);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve style from a tag with activated theme.
|
||||
*
|
||||
@@ -58,6 +119,18 @@ public class ThemeResolver {
|
||||
return theme.getSettings().styles().resolveTag(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve style from a tag with given theme.
|
||||
*
|
||||
* @param tag the tag
|
||||
* @param themeName the theme name
|
||||
* @return a style
|
||||
*/
|
||||
public String resolveStyleTag(String tag, String themeName) {
|
||||
Theme t = StringUtils.hasText(themeName) ? themeRegistry.get(themeName) : theme;
|
||||
return t.getSettings().styles().resolveTag(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve figure from a tag with activated theme.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-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.
|
||||
@@ -17,14 +17,21 @@ package org.springframework.shell.style;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
import org.jline.utils.AttributedStyle;
|
||||
import org.jline.utils.Colors;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.shell.component.view.screen.Color;
|
||||
import org.springframework.shell.style.ThemeResolver.ResolvedValues;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ThemeResolverTests {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
private ThemeResolver themeResolver;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ThemeRegistry themeRegistry = new ThemeRegistry();
|
||||
themeRegistry.register(new Theme() {
|
||||
@Override
|
||||
@@ -37,10 +44,55 @@ public class ThemeResolverTests {
|
||||
return ThemeSettings.defaults();
|
||||
}
|
||||
});
|
||||
ThemeResolver themeResolver = new ThemeResolver(themeRegistry, "default");
|
||||
themeResolver = new ThemeResolver(themeRegistry, "default");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void styleExpression() {
|
||||
assertThat(themeResolver.resolveStyleTag(StyleSettings.TAG_TITLE)).isEqualTo("bold");
|
||||
assertThat(themeResolver.resolveStyle("bold")).isEqualTo(AttributedStyle.BOLD);
|
||||
assertThat(themeResolver.evaluateExpression("@{bold foo}"))
|
||||
.isEqualTo(new AttributedString("foo", AttributedStyle.BOLD));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveValuesIndStyle() {
|
||||
AttributedStyle s = AttributedStyle.DEFAULT.background(AttributedStyle.BLUE).foreground(AttributedStyle.RED);
|
||||
ResolvedValues resolvedValues = themeResolver.resolveValues(s);
|
||||
assertThat(resolvedValues.background()).isEqualTo(Colors.DEFAULT_COLORS_256[AttributedStyle.BLUE]);
|
||||
assertThat(resolvedValues.foreground()).isEqualTo(Colors.DEFAULT_COLORS_256[AttributedStyle.RED]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveValuesRgbStyle() {
|
||||
AttributedStyle s = AttributedStyle.DEFAULT.backgroundRgb(Color.BLUE).foregroundRgb(Color.RED);
|
||||
ResolvedValues resolvedValues = themeResolver.resolveValues(s);
|
||||
assertThat(resolvedValues.background()).isEqualTo(Color.BLUE);
|
||||
assertThat(resolvedValues.foreground()).isEqualTo(Color.RED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveValuesRgbExp() {
|
||||
AttributedStyle s = themeResolver.resolveStyle("bg-rgb:#0000FF,fg-rgb:#FF0000");
|
||||
ResolvedValues resolvedValues = themeResolver.resolveValues(s);
|
||||
assertThat(resolvedValues.background()).isEqualTo(Color.BLUE);
|
||||
assertThat(resolvedValues.foreground()).isEqualTo(Color.RED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveValuesIndExp() {
|
||||
AttributedStyle s = themeResolver.resolveStyle("bg:blue,fg:red");
|
||||
ResolvedValues resolvedValues = themeResolver.resolveValues(s);
|
||||
assertThat(resolvedValues.background()).isEqualTo(Colors.DEFAULT_COLORS_256[AttributedStyle.BLUE]);
|
||||
assertThat(resolvedValues.foreground()).isEqualTo(Colors.DEFAULT_COLORS_256[AttributedStyle.RED]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveValuesNotDefinedExp() {
|
||||
AttributedStyle s = themeResolver.resolveStyle("");
|
||||
ResolvedValues resolvedValues = themeResolver.resolveValues(s);
|
||||
assertThat(resolvedValues.background()).isEqualTo(-1);
|
||||
assertThat(resolvedValues.foreground()).isEqualTo(-1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-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.
|
||||
@@ -53,10 +53,11 @@ public class ThemeSettingsTests {
|
||||
StyleSettings.TAG_ITEM_SELECTED,
|
||||
StyleSettings.TAG_ITEM_UNSELECTED,
|
||||
StyleSettings.TAG_ITEM_SELECTOR,
|
||||
StyleSettings.TAG_HIGHLIGHT })
|
||||
StyleSettings.TAG_HIGHLIGHT,
|
||||
StyleSettings.TAG_BACKGROUND })
|
||||
public void testTags(String tag) {
|
||||
ThemeSettings themeSettings = ThemeSettings.defaults();
|
||||
String resolveTag = themeSettings.styles().resolveTag(StyleSettings.TAG_TITLE);
|
||||
String resolveTag = themeSettings.styles().resolveTag(tag);
|
||||
assertThat(resolveTag).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user