feat(theme): enhance theme management with storage capabilities and add unit tests
This commit is contained in:
@@ -1,13 +1,21 @@
|
|||||||
package de.extio.game_engine.renderer;
|
package de.extio.game_engine.renderer;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import de.extio.game_engine.resource.StaticResource;
|
||||||
import de.extio.game_engine.renderer.g2d.theme.Theme;
|
import de.extio.game_engine.renderer.g2d.theme.Theme;
|
||||||
|
|
||||||
public interface ThemeManager {
|
public interface ThemeManager {
|
||||||
|
|
||||||
List<String> getAvailableThemeNames();
|
List<String> getAvailableThemeNames();
|
||||||
|
|
||||||
|
Optional<Theme> loadThemeFromStorage(String themeName);
|
||||||
|
|
||||||
|
void saveThemeToStorage(Theme theme);
|
||||||
|
|
||||||
|
Optional<Theme> loadThemeFromStaticResource(StaticResource themeResource);
|
||||||
|
|
||||||
Theme getCurrentTheme();
|
Theme getCurrentTheme();
|
||||||
|
|
||||||
void setCurrentTheme(Theme theme);
|
void setCurrentTheme(Theme theme);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import java.util.Map;
|
|||||||
|
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -26,6 +25,7 @@ import de.extio.game_engine.renderer.g2d.theme.Theme;
|
|||||||
import de.extio.game_engine.renderer.g2d.theme.UrbanThemeFactoryBean;
|
import de.extio.game_engine.renderer.g2d.theme.UrbanThemeFactoryBean;
|
||||||
import de.extio.game_engine.renderer.g2d.theme.VintageThemeFactoryBean;
|
import de.extio.game_engine.renderer.g2d.theme.VintageThemeFactoryBean;
|
||||||
import de.extio.game_engine.resource.StaticResourceService;
|
import de.extio.game_engine.resource.StaticResourceService;
|
||||||
|
import de.extio.game_engine.storage.StorageService;
|
||||||
|
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@ConditionalOnProperty(name = "game-engine.renderer.enabled", havingValue = "true", matchIfMissing = true)
|
@ConditionalOnProperty(name = "game-engine.renderer.enabled", havingValue = "true", matchIfMissing = true)
|
||||||
@@ -113,10 +113,11 @@ public class G2DAutoConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
G2DThemeManager g2dThemeManager(
|
G2DThemeManager g2dThemeManager(
|
||||||
final StaticResourceService staticResourceService,
|
final StaticResourceService staticResourceService,
|
||||||
|
final StorageService storageService,
|
||||||
final List<PatternRenderer> patternRendererList,
|
final List<PatternRenderer> patternRendererList,
|
||||||
final Map<String, Theme> themes,
|
final Map<String, Theme> themes,
|
||||||
@Qualifier("spacecraftTheme") final Theme defaultTheme) {
|
@Value("${game-engine.renderer.default-theme:urbanTheme}") final String defaultThemeName) {
|
||||||
return new G2DThemeManager(staticResourceService, patternRendererList, themes, defaultTheme);
|
return new G2DThemeManager(staticResourceService, storageService, patternRendererList, themes, defaultThemeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package de.extio.game_engine.renderer.g2d.theme;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -11,15 +13,21 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import de.extio.game_engine.renderer.ThemeManager;
|
import de.extio.game_engine.renderer.ThemeManager;
|
||||||
import de.extio.game_engine.renderer.g2d.G2DRenderer;
|
import de.extio.game_engine.renderer.g2d.G2DRenderer;
|
||||||
|
import de.extio.game_engine.resource.StaticResource;
|
||||||
import de.extio.game_engine.resource.StaticResourceService;
|
import de.extio.game_engine.resource.StaticResourceService;
|
||||||
|
import de.extio.game_engine.storage.StorageService;
|
||||||
|
|
||||||
public class G2DThemeManager implements ThemeManager {
|
public class G2DThemeManager implements ThemeManager {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(G2DThemeManager.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(G2DThemeManager.class);
|
||||||
|
|
||||||
|
private static final List<String> THEME_STORAGE_PATH = List.of("gameEngine", "themes");
|
||||||
|
private static final String THEME_STORAGE_NAME = "lastTheme";
|
||||||
|
|
||||||
private G2DRenderer g2dRenderer;
|
private G2DRenderer g2dRenderer;
|
||||||
|
|
||||||
private final StaticResourceService staticResourceService;
|
private final StaticResourceService staticResourceService;
|
||||||
|
private final StorageService storageService;
|
||||||
|
|
||||||
private final Map<String, PatternRenderer> patternRenderers;
|
private final Map<String, PatternRenderer> patternRenderers;
|
||||||
private final Map<String, Theme> themes;
|
private final Map<String, Theme> themes;
|
||||||
@@ -29,20 +37,48 @@ public class G2DThemeManager implements ThemeManager {
|
|||||||
|
|
||||||
public G2DThemeManager(
|
public G2DThemeManager(
|
||||||
final StaticResourceService staticResourceService,
|
final StaticResourceService staticResourceService,
|
||||||
|
final StorageService storageService,
|
||||||
final List<PatternRenderer> patternRendererList,
|
final List<PatternRenderer> patternRendererList,
|
||||||
final Map<String, Theme> themes,
|
final Map<String, Theme> themes,
|
||||||
final Theme defaultTheme) {
|
final String defaultThemeName) {
|
||||||
this.staticResourceService = staticResourceService;
|
this.staticResourceService = staticResourceService;
|
||||||
|
this.storageService = storageService;
|
||||||
this.patternRenderers = patternRendererList.stream()
|
this.patternRenderers = patternRendererList.stream()
|
||||||
.collect(Collectors.toMap(
|
.collect(Collectors.toMap(
|
||||||
renderer -> renderer.getClass().getSimpleName().substring(0, 1).toLowerCase() + renderer.getClass().getSimpleName().substring(1),
|
renderer -> renderer.getClass().getSimpleName().substring(0, 1).toLowerCase() + renderer.getClass().getSimpleName().substring(1),
|
||||||
Function.identity()));
|
Function.identity()));
|
||||||
this.themes = Objects.requireNonNull(themes, "themes cannot be null");
|
this.themes = new ConcurrentHashMap<>(Objects.requireNonNull(themes, "themes cannot be null"));
|
||||||
this.defaultTheme = Objects.requireNonNull(defaultTheme, "defaultTheme cannot be null");
|
this.defaultTheme = Objects.requireNonNull(this.themes.get(defaultThemeName), "defaultTheme cannot be null");
|
||||||
|
|
||||||
LOGGER.info("G2DThemeManager initialized with {} pattern renderers: {}", this.patternRenderers.size(), this.patternRenderers.keySet());
|
LOGGER.info("G2DThemeManager initialized with {} pattern renderers: {}", this.patternRenderers.size(), this.patternRenderers.keySet());
|
||||||
LOGGER.info("G2DThemeManager initialized with {} themes: {}", this.themes.size(), this.themes.keySet());
|
LOGGER.info("G2DThemeManager initialized with {} themes: {}", this.themes.size(), this.themes.keySet());
|
||||||
this.currentTheme = this.defaultTheme;
|
this.currentTheme = this.defaultTheme;
|
||||||
|
this.loadLastUsedTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerTheme(final Theme theme) {
|
||||||
|
if (theme == null || theme.getName() == null || theme.getName().isBlank()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.themes.put(theme.getName(), theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadLastUsedTheme() {
|
||||||
|
if (this.storageService == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final Optional<Theme> persistedTheme = this.storageService.loadByPath(Theme.class, THEME_STORAGE_PATH, THEME_STORAGE_NAME);
|
||||||
|
if (persistedTheme.isPresent()) {
|
||||||
|
this.currentTheme = persistedTheme.get();
|
||||||
|
this.registerTheme(this.currentTheme);
|
||||||
|
LOGGER.info("Loaded last used theme: {}", this.currentTheme.getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (final Exception e) {
|
||||||
|
LOGGER.warn("Could not load last used theme. Using default.", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -54,6 +90,44 @@ public class G2DThemeManager implements ThemeManager {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Theme> loadThemeFromStorage(final String themeName) {
|
||||||
|
if (themeName == null || themeName.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("themeName cannot be null/blank");
|
||||||
|
}
|
||||||
|
if (this.storageService == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
final Optional<Theme> loaded = this.storageService.loadByPath(Theme.class, THEME_STORAGE_PATH, themeName);
|
||||||
|
loaded.ifPresent(this::registerTheme);
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveThemeToStorage(final Theme theme) {
|
||||||
|
if (theme == null) {
|
||||||
|
throw new IllegalArgumentException("theme cannot be null");
|
||||||
|
}
|
||||||
|
if (theme.getName() == null || theme.getName().isBlank()) {
|
||||||
|
throw new IllegalArgumentException("theme.name cannot be null/blank");
|
||||||
|
}
|
||||||
|
if (this.storageService == null) {
|
||||||
|
throw new IllegalStateException("storageService not available");
|
||||||
|
}
|
||||||
|
this.storageService.store(THEME_STORAGE_PATH, theme.getName(), theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Theme> loadThemeFromStaticResource(final StaticResource themeResource) {
|
||||||
|
Objects.requireNonNull(themeResource, "themeResource");
|
||||||
|
if (this.staticResourceService == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
final Optional<Theme> loaded = this.staticResourceService.loadByPath(Theme.class, themeResource);
|
||||||
|
loaded.ifPresent(this::registerTheme);
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setCurrentTheme(final String themeName) {
|
public void setCurrentTheme(final String themeName) {
|
||||||
if (themeName == null || themeName.isBlank()) {
|
if (themeName == null || themeName.isBlank()) {
|
||||||
@@ -94,14 +168,40 @@ public class G2DThemeManager implements ThemeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.currentTheme = theme;
|
this.currentTheme = theme;
|
||||||
|
this.registerTheme(theme);
|
||||||
LOGGER.info("Theme switched to: {}", theme.getName());
|
LOGGER.info("Theme switched to: {}", theme.getName());
|
||||||
|
this.persistLastUsedTheme(theme);
|
||||||
|
|
||||||
this.g2dRenderer.reset();
|
if (this.g2dRenderer != null) {
|
||||||
|
this.g2dRenderer.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistLastUsedTheme(final Theme theme) {
|
||||||
|
if (this.storageService == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.storageService.store(THEME_STORAGE_PATH, THEME_STORAGE_NAME, theme);
|
||||||
|
}
|
||||||
|
catch (final Exception e) {
|
||||||
|
LOGGER.warn("Could not persist last used theme", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Theme loadTheme(final String themeName) {
|
public Theme loadTheme(final String themeName) {
|
||||||
Objects.requireNonNull(this.staticResourceService, "staticResourceService");
|
try {
|
||||||
LOGGER.warn("Theme loading from StaticResourceService not yet implemented. Requested theme: {}. Using default theme.", themeName);
|
final Optional<Theme> fromStorage = this.loadThemeFromStorage(themeName);
|
||||||
|
if (fromStorage.isPresent()) {
|
||||||
|
LOGGER.info("Loaded theme '{}' from storage", themeName);
|
||||||
|
return fromStorage.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (final Exception e) {
|
||||||
|
LOGGER.warn("Could not load theme '{}' from storage. Using default.", themeName, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.warn("Theme '{}' not found in storage. Using default theme.", themeName);
|
||||||
return this.defaultTheme;
|
return this.defaultTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ package de.extio.game_engine.renderer.g2d.theme;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a color in HSB (Hue, Saturation, Brightness) color space.
|
* Represents a color in HSB (Hue, Saturation, Brightness) color space.
|
||||||
* This allows for easier runtime color manipulation (e.g., brightening on hover).
|
* This allows for easier runtime color manipulation (e.g., brightening on hover).
|
||||||
@@ -20,7 +23,11 @@ public final class HSBColor {
|
|||||||
* @param saturation Saturation component (0.0 to 1.0)
|
* @param saturation Saturation component (0.0 to 1.0)
|
||||||
* @param brightness Brightness component (0.0 to 1.0)
|
* @param brightness Brightness component (0.0 to 1.0)
|
||||||
*/
|
*/
|
||||||
public HSBColor(final float hue, final float saturation, final float brightness) {
|
@JsonCreator
|
||||||
|
public HSBColor(
|
||||||
|
@JsonProperty("hue") final float hue,
|
||||||
|
@JsonProperty("saturation") final float saturation,
|
||||||
|
@JsonProperty("brightness") final float brightness) {
|
||||||
this.hue = clamp(hue);
|
this.hue = clamp(hue);
|
||||||
this.saturation = clamp(saturation);
|
this.saturation = clamp(saturation);
|
||||||
this.brightness = clamp(brightness);
|
this.brightness = clamp(brightness);
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package de.extio.game_engine.renderer.g2d.theme;
|
package de.extio.game_engine.renderer.g2d.theme;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a complete UI theme with all color definitions and pattern renderer.
|
* Represents a complete UI theme with all color definitions and pattern renderer.
|
||||||
* Themes are element-based rather than component-based, allowing consistent styling across different components.
|
* Themes are element-based rather than component-based, allowing consistent styling across different components.
|
||||||
@@ -33,21 +36,54 @@ public class Theme {
|
|||||||
private final float hoverBrightnessAdjustment;
|
private final float hoverBrightnessAdjustment;
|
||||||
private final float pressedBrightnessAdjustment;
|
private final float pressedBrightnessAdjustment;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public Theme(
|
||||||
|
@JsonProperty("name") final String name,
|
||||||
|
@JsonProperty("patternRendererName") final String patternRendererName,
|
||||||
|
@JsonProperty("borderOuter") final HSBColor borderOuter,
|
||||||
|
@JsonProperty("borderInner") final HSBColor borderInner,
|
||||||
|
@JsonProperty("borderInnerDisabled") final HSBColor borderInnerDisabled,
|
||||||
|
@JsonProperty("backgroundNormal") final HSBColor backgroundNormal,
|
||||||
|
@JsonProperty("backgroundSelected") final HSBColor backgroundSelected,
|
||||||
|
@JsonProperty("textNormal") final HSBColor textNormal,
|
||||||
|
@JsonProperty("textDisabled") final HSBColor textDisabled,
|
||||||
|
@JsonProperty("selectionPrimary") final HSBColor selectionPrimary,
|
||||||
|
@JsonProperty("selectionSecondary") final HSBColor selectionSecondary,
|
||||||
|
@JsonProperty("windowBackground") final HSBColor windowBackground,
|
||||||
|
@JsonProperty("hoverBrightnessAdjustment") final float hoverBrightnessAdjustment,
|
||||||
|
@JsonProperty("pressedBrightnessAdjustment") final float pressedBrightnessAdjustment) {
|
||||||
|
this.name = name;
|
||||||
|
this.patternRendererName = patternRendererName;
|
||||||
|
this.borderOuter = borderOuter;
|
||||||
|
this.borderInner = borderInner;
|
||||||
|
this.borderInnerDisabled = borderInnerDisabled;
|
||||||
|
this.backgroundNormal = backgroundNormal;
|
||||||
|
this.backgroundSelected = backgroundSelected;
|
||||||
|
this.textNormal = textNormal;
|
||||||
|
this.textDisabled = textDisabled;
|
||||||
|
this.selectionPrimary = selectionPrimary;
|
||||||
|
this.selectionSecondary = selectionSecondary;
|
||||||
|
this.windowBackground = windowBackground;
|
||||||
|
this.hoverBrightnessAdjustment = hoverBrightnessAdjustment;
|
||||||
|
this.pressedBrightnessAdjustment = pressedBrightnessAdjustment;
|
||||||
|
}
|
||||||
|
|
||||||
private Theme(final Builder builder) {
|
private Theme(final Builder builder) {
|
||||||
this.name = builder.name;
|
this(
|
||||||
this.patternRendererName = builder.patternRendererName;
|
builder.name,
|
||||||
this.borderOuter = builder.borderOuter;
|
builder.patternRendererName,
|
||||||
this.borderInner = builder.borderInner;
|
builder.borderOuter,
|
||||||
this.borderInnerDisabled = builder.borderInnerDisabled;
|
builder.borderInner,
|
||||||
this.backgroundNormal = builder.backgroundNormal;
|
builder.borderInnerDisabled,
|
||||||
this.backgroundSelected = builder.backgroundSelected;
|
builder.backgroundNormal,
|
||||||
this.textNormal = builder.textNormal;
|
builder.backgroundSelected,
|
||||||
this.textDisabled = builder.textDisabled;
|
builder.textNormal,
|
||||||
this.selectionPrimary = builder.selectionPrimary;
|
builder.textDisabled,
|
||||||
this.selectionSecondary = builder.selectionSecondary;
|
builder.selectionPrimary,
|
||||||
this.windowBackground = builder.windowBackground;
|
builder.selectionSecondary,
|
||||||
this.hoverBrightnessAdjustment = builder.hoverBrightnessAdjustment;
|
builder.windowBackground,
|
||||||
this.pressedBrightnessAdjustment = builder.pressedBrightnessAdjustment;
|
builder.hoverBrightnessAdjustment,
|
||||||
|
builder.pressedBrightnessAdjustment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import de.extio.game_engine.renderer.model.event.UiControlEvent;
|
|||||||
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
||||||
import de.extio.game_engine.resource.StaticResource;
|
import de.extio.game_engine.resource.StaticResource;
|
||||||
import de.extio.game_engine.spatial2.model.ImmutableCoordI2;
|
import de.extio.game_engine.spatial2.model.ImmutableCoordI2;
|
||||||
import de.extio.game_engine.storage.StorageService;
|
|
||||||
|
|
||||||
public class DemoModule extends AbstractClientModule {
|
public class DemoModule extends AbstractClientModule {
|
||||||
|
|
||||||
@@ -48,9 +47,6 @@ public class DemoModule extends AbstractClientModule {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ThemeManager themeManager;
|
private ThemeManager themeManager;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private StorageService storageService;
|
|
||||||
|
|
||||||
private Window mainWindow;
|
private Window mainWindow;
|
||||||
|
|
||||||
private Window secondaryWindow;
|
private Window secondaryWindow;
|
||||||
@@ -93,12 +89,6 @@ public class DemoModule extends AbstractClientModule {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivate() {
|
public void onActivate() {
|
||||||
this.storageService.loadByPath(String.class, List.of("gameEngine"), "theme").ifPresent(themeName -> {
|
|
||||||
if (themeName != null && !themeName.isBlank()) {
|
|
||||||
this.themeManager.setCurrentTheme(themeName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final var audioOptions = this.audioController.getAudioOptions();
|
final var audioOptions = this.audioController.getAudioOptions();
|
||||||
this.audioMuted = isAudioMuted(audioOptions);
|
this.audioMuted = isAudioMuted(audioOptions);
|
||||||
|
|
||||||
@@ -158,7 +148,6 @@ public class DemoModule extends AbstractClientModule {
|
|||||||
if (themeName != null) {
|
if (themeName != null) {
|
||||||
this.audioController.play(new StaticResource(List.of("audio"), "alert0.ogg"));
|
this.audioController.play(new StaticResource(List.of("audio"), "alert0.ogg"));
|
||||||
this.themeManager.setCurrentTheme(themeName);
|
this.themeManager.setCurrentTheme(themeName);
|
||||||
this.storageService.store(List.of("gameEngine"), "theme", themeName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,222 @@
|
|||||||
|
package de.extio.game_engine.renderer.g2d.theme;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import de.extio.game_engine.resource.StaticResource;
|
||||||
|
import de.extio.game_engine.resource.StaticResourceService;
|
||||||
|
import de.extio.game_engine.storage.StorageService;
|
||||||
|
|
||||||
|
public class G2DThemeManagerTest {
|
||||||
|
|
||||||
|
private static Theme theme(final String name) {
|
||||||
|
return Theme.builder()
|
||||||
|
.name(name)
|
||||||
|
.patternRendererName("testPatternRenderer")
|
||||||
|
.borderOuter(new HSBColor(0.1f, 0.2f, 0.3f))
|
||||||
|
.borderInner(new HSBColor(0.1f, 0.2f, 0.3f))
|
||||||
|
.borderInnerDisabled(new HSBColor(0.1f, 0.2f, 0.3f))
|
||||||
|
.backgroundNormal(new HSBColor(0.1f, 0.2f, 0.3f))
|
||||||
|
.backgroundSelected(new HSBColor(0.1f, 0.2f, 0.3f))
|
||||||
|
.textNormal(new HSBColor(0.1f, 0.2f, 0.3f))
|
||||||
|
.textDisabled(new HSBColor(0.1f, 0.2f, 0.3f))
|
||||||
|
.selectionPrimary(new HSBColor(0.1f, 0.2f, 0.3f))
|
||||||
|
.selectionSecondary(new HSBColor(0.1f, 0.2f, 0.3f))
|
||||||
|
.windowBackground(new HSBColor(0.1f, 0.2f, 0.3f))
|
||||||
|
.hoverBrightnessAdjustment(0.25f)
|
||||||
|
.pressedBrightnessAdjustment(0.40f)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void loadsLastUsedThemeFromStorageOnConstruction() {
|
||||||
|
final StaticResourceService staticResourceService = org.mockito.Mockito.mock(StaticResourceService.class);
|
||||||
|
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
||||||
|
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
||||||
|
|
||||||
|
final Theme defaultTheme = theme("Default");
|
||||||
|
final Theme persistedTheme = theme("Persisted");
|
||||||
|
|
||||||
|
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine", "themes")), eq("lastTheme")))
|
||||||
|
.thenReturn(Optional.of(persistedTheme));
|
||||||
|
|
||||||
|
final var manager = new G2DThemeManager(
|
||||||
|
staticResourceService,
|
||||||
|
storageService,
|
||||||
|
List.of(patternRenderer),
|
||||||
|
Map.of("defaultKey", defaultTheme),
|
||||||
|
"defaultKey");
|
||||||
|
|
||||||
|
assertEquals("Persisted", manager.getCurrentTheme().getName());
|
||||||
|
assertTrue(manager.getAvailableThemeNames().contains("Persisted"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void setCurrentThemePersistsLastThemeObject() {
|
||||||
|
final StaticResourceService staticResourceService = org.mockito.Mockito.mock(StaticResourceService.class);
|
||||||
|
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
||||||
|
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
||||||
|
|
||||||
|
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine", "themes")), eq("lastTheme")))
|
||||||
|
.thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
final Theme defaultTheme = theme("Default");
|
||||||
|
final var manager = new G2DThemeManager(
|
||||||
|
staticResourceService,
|
||||||
|
storageService,
|
||||||
|
List.of(patternRenderer),
|
||||||
|
Map.of("defaultKey", defaultTheme),
|
||||||
|
"defaultKey");
|
||||||
|
|
||||||
|
final Theme selected = theme("Selected");
|
||||||
|
manager.setCurrentTheme(selected);
|
||||||
|
|
||||||
|
verify(storageService).store(eq(List.of("gameEngine", "themes")), eq("lastTheme"), eq(selected));
|
||||||
|
assertTrue(manager.getAvailableThemeNames().contains("Selected"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void loadThemeFromStorageRegistersTheme() {
|
||||||
|
final StaticResourceService staticResourceService = org.mockito.Mockito.mock(StaticResourceService.class);
|
||||||
|
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
||||||
|
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
||||||
|
|
||||||
|
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine", "themes")), eq("lastTheme")))
|
||||||
|
.thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
final Theme defaultTheme = theme("Default");
|
||||||
|
final var manager = new G2DThemeManager(
|
||||||
|
staticResourceService,
|
||||||
|
storageService,
|
||||||
|
List.of(patternRenderer),
|
||||||
|
Map.of("defaultKey", defaultTheme),
|
||||||
|
"defaultKey");
|
||||||
|
|
||||||
|
final Theme loaded = theme("FromStorage");
|
||||||
|
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine", "themes")), eq("FromStorage")))
|
||||||
|
.thenReturn(Optional.of(loaded));
|
||||||
|
|
||||||
|
final Optional<Theme> result = manager.loadThemeFromStorage("FromStorage");
|
||||||
|
assertTrue(result.isPresent());
|
||||||
|
assertEquals("FromStorage", result.get().getName());
|
||||||
|
assertTrue(manager.getAvailableThemeNames().contains("FromStorage"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void loadThemeFromStaticResourceRegistersTheme() {
|
||||||
|
final StaticResourceService staticResourceService = org.mockito.Mockito.mock(StaticResourceService.class);
|
||||||
|
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
||||||
|
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
||||||
|
|
||||||
|
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine", "themes")), eq("lastTheme")))
|
||||||
|
.thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
final Theme defaultTheme = theme("Default");
|
||||||
|
final var manager = new G2DThemeManager(
|
||||||
|
staticResourceService,
|
||||||
|
storageService,
|
||||||
|
List.of(patternRenderer),
|
||||||
|
Map.of("defaultKey", defaultTheme),
|
||||||
|
"defaultKey");
|
||||||
|
|
||||||
|
final Theme loaded = theme("FromStaticResource");
|
||||||
|
final StaticResource resource = new StaticResource(List.of("themes"), "fromStatic.theme");
|
||||||
|
when(staticResourceService.loadByPath(eq(Theme.class), eq(resource)))
|
||||||
|
.thenReturn(Optional.of(loaded));
|
||||||
|
|
||||||
|
final Optional<Theme> result = manager.loadThemeFromStaticResource(resource);
|
||||||
|
assertTrue(result.isPresent());
|
||||||
|
assertEquals("FromStaticResource", result.get().getName());
|
||||||
|
assertTrue(manager.getAvailableThemeNames().contains("FromStaticResource"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void saveThemeToStorageUsesThemeNameAsStorageName() {
|
||||||
|
final StaticResourceService staticResourceService = org.mockito.Mockito.mock(StaticResourceService.class);
|
||||||
|
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
||||||
|
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
||||||
|
|
||||||
|
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine", "themes")), eq("lastTheme")))
|
||||||
|
.thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
final Theme defaultTheme = theme("Default");
|
||||||
|
final var manager = new G2DThemeManager(
|
||||||
|
staticResourceService,
|
||||||
|
storageService,
|
||||||
|
List.of(patternRenderer),
|
||||||
|
Map.of("defaultKey", defaultTheme),
|
||||||
|
"defaultKey");
|
||||||
|
|
||||||
|
final Theme themeToSave = theme("UserTheme");
|
||||||
|
manager.saveThemeToStorage(themeToSave);
|
||||||
|
|
||||||
|
verify(storageService).store(eq(List.of("gameEngine", "themes")), eq("UserTheme"), eq(themeToSave));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void saveThemeToStorageThrowsIfNoStorageService() {
|
||||||
|
final StaticResourceService staticResourceService = org.mockito.Mockito.mock(StaticResourceService.class);
|
||||||
|
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
||||||
|
|
||||||
|
final Theme defaultTheme = theme("Default");
|
||||||
|
final var manager = new G2DThemeManager(
|
||||||
|
staticResourceService,
|
||||||
|
null,
|
||||||
|
List.of(patternRenderer),
|
||||||
|
Map.of("defaultKey", defaultTheme),
|
||||||
|
"defaultKey");
|
||||||
|
|
||||||
|
assertThrows(IllegalStateException.class, () -> manager.saveThemeToStorage(theme("SomeTheme")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void loadThemeFromStorageReturnsEmptyWhenStorageNotAvailable() {
|
||||||
|
final StaticResourceService staticResourceService = org.mockito.Mockito.mock(StaticResourceService.class);
|
||||||
|
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
||||||
|
|
||||||
|
final Theme defaultTheme = theme("Default");
|
||||||
|
final var manager = new G2DThemeManager(
|
||||||
|
staticResourceService,
|
||||||
|
null,
|
||||||
|
List.of(patternRenderer),
|
||||||
|
Map.of("defaultKey", defaultTheme),
|
||||||
|
"defaultKey");
|
||||||
|
|
||||||
|
final Optional<Theme> loaded = manager.loadThemeFromStorage("Anything");
|
||||||
|
assertFalse(loaded.isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void loadThemeFromStaticResourceReturnsEmptyWhenStaticResourceServiceNotAvailable() {
|
||||||
|
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
||||||
|
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
||||||
|
|
||||||
|
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine", "themes")), eq("lastTheme")))
|
||||||
|
.thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
final Theme defaultTheme = theme("Default");
|
||||||
|
final var manager = new G2DThemeManager(
|
||||||
|
null,
|
||||||
|
storageService,
|
||||||
|
List.of(patternRenderer),
|
||||||
|
Map.of("defaultKey", defaultTheme),
|
||||||
|
"defaultKey");
|
||||||
|
|
||||||
|
final Optional<Theme> loaded = manager.loadThemeFromStaticResource(new StaticResource(List.of("themes"), "x"));
|
||||||
|
assertFalse(loaded.isPresent());
|
||||||
|
verify(storageService, never()).store(any(), any(), any());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user