Added OptionsModule
This commit is contained in:
105
src/main/java/de/extio/game_engine/menu/AbstractOptionsTab.java
Normal file
105
src/main/java/de/extio/game_engine/menu/AbstractOptionsTab.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package de.extio.game_engine.menu;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import de.extio.game_engine.audio.AudioControl;
|
||||
import de.extio.game_engine.event.EventService;
|
||||
import de.extio.game_engine.i18n.LocalizationService;
|
||||
import de.extio.game_engine.keyboard.KeycodeRegistry;
|
||||
import de.extio.game_engine.renderer.RendererControl;
|
||||
import de.extio.game_engine.renderer.ThemeManager;
|
||||
import de.extio.game_engine.renderer.container.ScrollArea;
|
||||
import de.extio.game_engine.renderer.container.Window;
|
||||
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
||||
import de.extio.game_engine.storage.StorageService;
|
||||
|
||||
public abstract class AbstractOptionsTab {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
private RenderingBoPool renderingBoPool;
|
||||
|
||||
@Autowired
|
||||
private EventService eventService;
|
||||
|
||||
@Autowired
|
||||
private LocalizationService localizationService;
|
||||
|
||||
@Autowired
|
||||
private RendererControl rendererControl;
|
||||
|
||||
@Autowired
|
||||
private AudioControl audioControl;
|
||||
|
||||
@Autowired
|
||||
private ThemeManager themeManager;
|
||||
|
||||
@Autowired
|
||||
private StorageService storageService;
|
||||
|
||||
@Autowired
|
||||
private KeycodeRegistry keycodeRegistry;
|
||||
|
||||
protected final ApplicationContext applicationContext() {
|
||||
return this.applicationContext;
|
||||
}
|
||||
|
||||
protected final RenderingBoPool renderingBoPool() {
|
||||
return this.renderingBoPool;
|
||||
}
|
||||
|
||||
protected final EventService eventService() {
|
||||
return this.eventService;
|
||||
}
|
||||
|
||||
protected final LocalizationService localizationService() {
|
||||
return this.localizationService;
|
||||
}
|
||||
|
||||
protected final RendererControl rendererControl() {
|
||||
return this.rendererControl;
|
||||
}
|
||||
|
||||
protected final AudioControl audioControl() {
|
||||
return this.audioControl;
|
||||
}
|
||||
|
||||
protected final ThemeManager themeManager() {
|
||||
return this.themeManager;
|
||||
}
|
||||
|
||||
protected final StorageService storageService() {
|
||||
return this.storageService;
|
||||
}
|
||||
|
||||
protected final KeycodeRegistry keycodeRegistry() {
|
||||
return this.keycodeRegistry;
|
||||
}
|
||||
|
||||
protected final Window optionsWindow(final OptionsModuleContext context) {
|
||||
return context.optionsWindow();
|
||||
}
|
||||
|
||||
protected final ScrollArea contentScrollArea(final OptionsModuleContext context) {
|
||||
return context.contentScrollArea();
|
||||
}
|
||||
|
||||
protected final void refreshContent(final OptionsModuleContext context) {
|
||||
context.refreshContent();
|
||||
}
|
||||
|
||||
protected final void rebuildWindow(final OptionsModuleContext context) {
|
||||
context.rebuildWindow();
|
||||
}
|
||||
|
||||
protected final int contentWidth(final OptionsModuleContext context) {
|
||||
return context.contentWidth();
|
||||
}
|
||||
|
||||
protected static String sanitizeId(final String input) {
|
||||
return input.replaceAll("[^a-zA-Z0-9]", "_");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
package de.extio.game_engine.menu;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import de.extio.game_engine.event.Event;
|
||||
import de.extio.game_engine.event.EventService;
|
||||
import de.extio.game_engine.module.AbstractClientModule;
|
||||
import de.extio.game_engine.renderer.RendererControl;
|
||||
import de.extio.game_engine.renderer.container.Window;
|
||||
import de.extio.game_engine.renderer.model.RenderingBoLayer;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.ButtonControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.LabelControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.LabelData;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.SliderControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.SliderData;
|
||||
import de.extio.game_engine.renderer.model.bo.HorizontalAlignment;
|
||||
import de.extio.game_engine.renderer.model.color.HSBColor;
|
||||
import de.extio.game_engine.renderer.model.color.ImmutableRgbaColor;
|
||||
import de.extio.game_engine.renderer.model.event.UiControlEvent;
|
||||
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
||||
import de.extio.game_engine.spatial2.model.ImmutableCoordI2;
|
||||
|
||||
public class HsbColorPickerModule extends AbstractClientModule {
|
||||
|
||||
public record HsbColorPickerResponseEvent(String requestId, boolean confirmed, HSBColor color) implements Event {
|
||||
}
|
||||
|
||||
private static final int WINDOW_WIDTH = 700;
|
||||
|
||||
private static final int WINDOW_HEIGHT = 520;
|
||||
|
||||
private static final int LABEL_HEIGHT = 34;
|
||||
|
||||
private static final int SLIDER_HEIGHT = 34;
|
||||
|
||||
private static final int BUTTON_HEIGHT = 40;
|
||||
|
||||
private static final int BUTTON_WIDTH = 110;
|
||||
|
||||
private static final int SPACING = 12;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
private RenderingBoPool renderingBoPool;
|
||||
|
||||
@Autowired
|
||||
private EventService eventService;
|
||||
|
||||
private Window dialogWindow;
|
||||
|
||||
private String requestId;
|
||||
|
||||
private String title;
|
||||
|
||||
private float hue;
|
||||
|
||||
private float saturation;
|
||||
|
||||
private float brightness;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.dialogWindow = this.applicationContext.getBean(Window.class);
|
||||
this.dialogWindow.setNormalizedDimension(ImmutableCoordI2.create(WINDOW_WIDTH, WINDOW_HEIGHT));
|
||||
this.dialogWindow.setNormalizedPosition(this.centeredPosition(this.dialogWindow.getNormalizedDimension()));
|
||||
this.dialogWindow.setDraggable(true);
|
||||
this.dialogWindow.setCloseButton(true);
|
||||
this.dialogWindow.setOnCloseAction(this::onCancel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnload() {
|
||||
this.getModuleService().unloadModule(this.dialogWindow.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivate() {
|
||||
this.setupDialog();
|
||||
this.getModuleService().changeActiveState(this.dialogWindow.getId(), true);
|
||||
this.eventService.register(UiControlEvent.class, this.getId(), this::onUiControlEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeactivate() {
|
||||
this.eventService.unregisterAll(this.getId());
|
||||
this.getModuleService().changeActiveState(this.dialogWindow.getId(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShow() {
|
||||
this.getModuleService().changeDisplayState(this.dialogWindow.getId(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHide() {
|
||||
this.getModuleService().changeDisplayState(this.dialogWindow.getId(), false);
|
||||
}
|
||||
|
||||
public void open(final String requestId, final String title, final HSBColor initialColor, final Window parentWindow) {
|
||||
this.requestId = requestId;
|
||||
this.title = title;
|
||||
if (initialColor != null) {
|
||||
this.hue = initialColor.getHue();
|
||||
this.saturation = initialColor.getSaturation();
|
||||
this.brightness = initialColor.getBrightness();
|
||||
}
|
||||
else {
|
||||
this.hue = 0.0f;
|
||||
this.saturation = 0.0f;
|
||||
this.brightness = 0.0f;
|
||||
}
|
||||
this.dialogWindow.setParent(parentWindow);
|
||||
this.getModuleService().changeActiveState(this.getId(), true);
|
||||
this.getModuleService().changeDisplayState(this.getId(), true);
|
||||
}
|
||||
|
||||
private void onUiControlEvent(final UiControlEvent event) {
|
||||
if (event.getId() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.getId()) {
|
||||
case "HsbColorPicker_Button_Ok" -> this.onOk();
|
||||
case "HsbColorPicker_Button_Cancel" -> this.onCancel();
|
||||
case "HsbColorPicker_Slider_Hue" -> this.updateSliderValue(event, SliderTarget.HUE);
|
||||
case "HsbColorPicker_Slider_Saturation" -> this.updateSliderValue(event, SliderTarget.SATURATION);
|
||||
case "HsbColorPicker_Slider_Brightness" -> this.updateSliderValue(event, SliderTarget.BRIGHTNESS);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSliderValue(final UiControlEvent event, final SliderTarget target) {
|
||||
if (!(event.getPayload() instanceof final Double rawValue)) {
|
||||
return;
|
||||
}
|
||||
final float value = Math.max(0.0f, Math.min(1.0f, rawValue.floatValue()));
|
||||
switch (target) {
|
||||
case HUE -> this.hue = value;
|
||||
case SATURATION -> this.saturation = value;
|
||||
case BRIGHTNESS -> this.brightness = value;
|
||||
}
|
||||
this.refreshPreview();
|
||||
}
|
||||
|
||||
private void setupDialog() {
|
||||
this.dialogWindow.clearRenderingBos();
|
||||
|
||||
final var contentWidth = WINDOW_WIDTH - Window.MARGIN_LEFT - Window.MARGIN_RIGHT;
|
||||
var yOffset = Window.MARGIN_TOP;
|
||||
|
||||
if (this.title != null && !this.title.isBlank()) {
|
||||
final var titleBo = this.renderingBoPool.acquire("HsbColorPicker_Title", ControlRenderingBo.class)
|
||||
.setType(LabelControl.class)
|
||||
.setCaption(this.title)
|
||||
.setFontSize(28)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.CENTER))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(contentWidth, LABEL_HEIGHT)
|
||||
.withPositionRelative(Window.MARGIN_LEFT, yOffset)
|
||||
.setLayer(RenderingBoLayer.UI0);
|
||||
this.dialogWindow.putRenderingBo(titleBo);
|
||||
yOffset += LABEL_HEIGHT + SPACING;
|
||||
}
|
||||
|
||||
final var preview = this.renderingBoPool.acquire("HsbColorPicker_Preview", ControlRenderingBo.class)
|
||||
.setType(LabelControl.class)
|
||||
.setCaption(" ")
|
||||
.setFontSize(20)
|
||||
.setControlData(new LabelData(this.toRgbaColor(this.getCurrentColor()), null, HorizontalAlignment.CENTER))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(contentWidth, 80)
|
||||
.withPositionRelative(Window.MARGIN_LEFT, yOffset)
|
||||
.setLayer(RenderingBoLayer.UI0);
|
||||
this.dialogWindow.putRenderingBo(preview);
|
||||
|
||||
yOffset += 80 + SPACING * 2;
|
||||
|
||||
yOffset = this.addSliderRow("Hue", "HsbColorPicker_Slider_Hue", "HsbColorPicker_Value_Hue", this.hue, yOffset, contentWidth);
|
||||
yOffset = this.addSliderRow("Saturation", "HsbColorPicker_Slider_Saturation", "HsbColorPicker_Value_Saturation", this.saturation, yOffset, contentWidth);
|
||||
yOffset = this.addSliderRow("Brightness", "HsbColorPicker_Slider_Brightness", "HsbColorPicker_Value_Brightness", this.brightness, yOffset, contentWidth);
|
||||
|
||||
yOffset += SPACING * 2;
|
||||
|
||||
final var buttonY = yOffset;
|
||||
final var okButton = this.renderingBoPool.acquire("HsbColorPicker_Button_Ok", ControlRenderingBo.class)
|
||||
.setType(ButtonControl.class)
|
||||
.setCaption("OK")
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(BUTTON_WIDTH, BUTTON_HEIGHT)
|
||||
.withPositionRelative(Window.MARGIN_LEFT + contentWidth - (BUTTON_WIDTH * 2 + SPACING), buttonY)
|
||||
.setLayer(RenderingBoLayer.UI0);
|
||||
this.dialogWindow.putRenderingBo(okButton);
|
||||
|
||||
final var cancelButton = this.renderingBoPool.acquire("HsbColorPicker_Button_Cancel", ControlRenderingBo.class)
|
||||
.setType(ButtonControl.class)
|
||||
.setCaption("Cancel")
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(BUTTON_WIDTH, BUTTON_HEIGHT)
|
||||
.withPositionRelative(Window.MARGIN_LEFT + contentWidth - BUTTON_WIDTH, buttonY)
|
||||
.setLayer(RenderingBoLayer.UI0);
|
||||
this.dialogWindow.putRenderingBo(cancelButton);
|
||||
|
||||
this.dialogWindow.draw();
|
||||
}
|
||||
|
||||
private int addSliderRow(final String label, final String sliderId, final String valueId, final float value, final int yOffset, final int contentWidth) {
|
||||
final var labelWidth = 160;
|
||||
final var valueWidth = 100;
|
||||
final var sliderWidth = contentWidth - labelWidth - valueWidth - SPACING * 2;
|
||||
|
||||
var bo = this.renderingBoPool.acquire("HsbColorPicker_Label_" + label, ControlRenderingBo.class)
|
||||
.setType(LabelControl.class)
|
||||
.setCaption(label)
|
||||
.setFontSize(24)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(labelWidth, LABEL_HEIGHT)
|
||||
.withPositionRelative(Window.MARGIN_LEFT, yOffset)
|
||||
.setLayer(RenderingBoLayer.UI0);
|
||||
this.dialogWindow.putRenderingBo(bo);
|
||||
|
||||
bo = this.renderingBoPool.acquire(sliderId, ControlRenderingBo.class)
|
||||
.setType(SliderControl.class)
|
||||
.setControlData(new SliderData(true, value, value, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(sliderWidth, SLIDER_HEIGHT)
|
||||
.withPositionRelative(Window.MARGIN_LEFT + labelWidth + SPACING, yOffset)
|
||||
.setLayer(RenderingBoLayer.UI0);
|
||||
this.dialogWindow.putRenderingBo(bo);
|
||||
|
||||
bo = this.renderingBoPool.acquire(valueId, ControlRenderingBo.class)
|
||||
.setType(LabelControl.class)
|
||||
.setCaption(this.formatValue(value))
|
||||
.setFontSize(22)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(valueWidth, LABEL_HEIGHT)
|
||||
.withPositionRelative(Window.MARGIN_LEFT + labelWidth + SPACING + sliderWidth + SPACING, yOffset)
|
||||
.setLayer(RenderingBoLayer.UI0);
|
||||
this.dialogWindow.putRenderingBo(bo);
|
||||
|
||||
return yOffset + LABEL_HEIGHT + SPACING;
|
||||
}
|
||||
|
||||
private void refreshPreview() {
|
||||
final var preview = this.dialogWindow.getRenderingBo("HsbColorPicker_Preview", ControlRenderingBo.class);
|
||||
if (preview != null) {
|
||||
preview.setControlData(new LabelData(this.toRgbaColor(this.getCurrentColor()), null, HorizontalAlignment.CENTER));
|
||||
this.dialogWindow.putRenderingBo(preview);
|
||||
}
|
||||
this.updateValueLabel("HsbColorPicker_Value_Hue", this.hue);
|
||||
this.updateValueLabel("HsbColorPicker_Value_Saturation", this.saturation);
|
||||
this.updateValueLabel("HsbColorPicker_Value_Brightness", this.brightness);
|
||||
this.dialogWindow.draw();
|
||||
}
|
||||
|
||||
private void updateValueLabel(final String id, final float value) {
|
||||
final var bo = this.dialogWindow.getRenderingBo(id, ControlRenderingBo.class);
|
||||
if (bo != null) {
|
||||
bo.setCaption(this.formatValue(value));
|
||||
this.dialogWindow.putRenderingBo(bo);
|
||||
}
|
||||
}
|
||||
|
||||
private String formatValue(final float value) {
|
||||
return String.format("%.2f", value);
|
||||
}
|
||||
|
||||
private HSBColor getCurrentColor() {
|
||||
return new HSBColor(this.hue, this.saturation, this.brightness);
|
||||
}
|
||||
|
||||
private ImmutableRgbaColor toRgbaColor(final HSBColor color) {
|
||||
return new ImmutableRgbaColor(color.toColor());
|
||||
}
|
||||
|
||||
private void onOk() {
|
||||
this.eventService.fire(new HsbColorPickerResponseEvent(this.requestId, true, this.getCurrentColor()));
|
||||
this.close();
|
||||
}
|
||||
|
||||
private void onCancel() {
|
||||
this.eventService.fire(new HsbColorPickerResponseEvent(this.requestId, false, null));
|
||||
this.close();
|
||||
}
|
||||
|
||||
private void close() {
|
||||
this.getModuleService().changeActiveState(this.getId(), false);
|
||||
this.getModuleService().changeDisplayState(this.getId(), false);
|
||||
}
|
||||
|
||||
private ImmutableCoordI2 centeredPosition(final de.extio.game_engine.spatial2.model.CoordI2 dimension) {
|
||||
final var referenceResolution = RendererControl.REFERENCE_RESOLUTION;
|
||||
final var x = (referenceResolution.getX() - dimension.getX()) / 2;
|
||||
final var y = (referenceResolution.getY() - dimension.getY()) / 2;
|
||||
return ImmutableCoordI2.create(x, y);
|
||||
}
|
||||
|
||||
private enum SliderTarget {
|
||||
HUE,
|
||||
SATURATION,
|
||||
BRIGHTNESS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package de.extio.game_engine.menu;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import de.extio.game_engine.event.EventService;
|
||||
import de.extio.game_engine.i18n.LocalizationService;
|
||||
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
||||
|
||||
@AutoConfiguration
|
||||
@ConditionalOnProperty(name = "game-engine.menu.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class MenuAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(name = "game-engine.menu.options.enabled", havingValue = "true", matchIfMissing = true)
|
||||
OptionsModule optionsModule(final ApplicationContext applicationContext, final RenderingBoPool renderingBoPool, final EventService eventService,
|
||||
final LocalizationService localizationService) {
|
||||
return new OptionsModule(applicationContext, renderingBoPool, eventService, localizationService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(name = "game-engine.menu.options.enabled", havingValue = "true", matchIfMissing = true)
|
||||
OptionsAudioTab optionsAudioTab() {
|
||||
return new OptionsAudioTab();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(name = "game-engine.menu.options.enabled", havingValue = "true", matchIfMissing = true)
|
||||
OptionsVideoTab optionsVideoTab() {
|
||||
return new OptionsVideoTab();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(name = "game-engine.menu.options.enabled", havingValue = "true", matchIfMissing = true)
|
||||
OptionsKeyboardTab optionsKeyboardTab() {
|
||||
return new OptionsKeyboardTab();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(name = "game-engine.menu.options.enabled", havingValue = "true", matchIfMissing = true)
|
||||
OptionsThemesTab optionsThemesTab() {
|
||||
return new OptionsThemesTab();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(name = "game-engine.menu.hsb-color-picker.enabled", havingValue = "true", matchIfMissing = true)
|
||||
HsbColorPickerModule hsbColorPickerModule() {
|
||||
return new HsbColorPickerModule();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(name = "game-engine.menu.theme-editor.enabled", havingValue = "true", matchIfMissing = true)
|
||||
@ConditionalOnProperty(name = "game-engine.menu.hsb-color-picker.enabled", havingValue = "true", matchIfMissing = true)
|
||||
ThemeEditorModule themeEditorModule() {
|
||||
return new ThemeEditorModule();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package de.extio.game_engine.menu;
|
||||
|
||||
import de.extio.game_engine.event.Event;
|
||||
|
||||
public record OpenOptionsModuleEvent(String tab) implements Event {
|
||||
|
||||
}
|
||||
208
src/main/java/de/extio/game_engine/menu/OptionsAudioTab.java
Normal file
208
src/main/java/de/extio/game_engine/menu/OptionsAudioTab.java
Normal file
@@ -0,0 +1,208 @@
|
||||
package de.extio.game_engine.menu;
|
||||
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.LabelControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.LabelData;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.SliderControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.SliderData;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.SwitchControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.SwitchData;
|
||||
import de.extio.game_engine.renderer.model.bo.HorizontalAlignment;
|
||||
import de.extio.game_engine.renderer.model.event.UiControlEvent;
|
||||
|
||||
final class OptionsAudioTab extends AbstractOptionsTab implements OptionsTab {
|
||||
|
||||
static final String TAB_ID = "audio";
|
||||
private static final String CONTROL_ID = "OptionsModule_Tab_Audio";
|
||||
|
||||
@Override
|
||||
public String tabId() {
|
||||
return TAB_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String controlId() {
|
||||
return CONTROL_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String navigationLocalizationKey() {
|
||||
return "ecyoa-85";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fallbackCaption() {
|
||||
return "Audio";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int navigationOrder() {
|
||||
return 40;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(final OptionsModuleContext context) {
|
||||
var yPos = 10;
|
||||
|
||||
var bo = this.renderingBoPool().acquire("OptionsModule_Audio_Label_SFX", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-97", "Sound Effects:"))
|
||||
.setFontSize(48)
|
||||
.setType(LabelControl.class)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(800, 60)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 60;
|
||||
|
||||
final var sfxEnabled = this.audioControl().getAudioOptions().getSfxOptions().isEnable();
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_Audio_SFX_Enable", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-98", "Enable"))
|
||||
.setFontSize(42)
|
||||
.setType(SwitchControl.class)
|
||||
.setControlData(new SwitchData(sfxEnabled, false, null, true))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(620, 60)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 80;
|
||||
|
||||
final var sfxVolume = this.audioControl().getAudioOptions().getSfxOptions().getVolume();
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_Audio_Label_SFX_Volume", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-99", "Volume:"))
|
||||
.setFontSize(42)
|
||||
.setType(LabelControl.class)
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(200, 60)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_Audio_SFX_Volume_Slider", ControlRenderingBo.class)
|
||||
.setCaption(String.format("%.2f", sfxVolume))
|
||||
.setFontSize(42)
|
||||
.setType(SliderControl.class)
|
||||
.setControlData(new SliderData(true, sfxVolume, 0.0, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(sfxEnabled)
|
||||
.withDimensionAbsolute(400, 60)
|
||||
.withPositionRelative(220, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 70;
|
||||
|
||||
final var sfxSoftwareMixing = this.audioControl().getAudioOptions().getSfxOptions().isSoftwareMixing();
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_Audio_SFX_SoftwareMixing", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-100", "Software Mixing"))
|
||||
.setFontSize(42)
|
||||
.setType(SwitchControl.class)
|
||||
.setControlData(new SwitchData(sfxSoftwareMixing, false, null, true))
|
||||
.setVisible(true)
|
||||
.setEnabled(sfxEnabled)
|
||||
.withDimensionAbsolute(620, 60)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 110;
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_Audio_Label_Music", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-101", "Music:"))
|
||||
.setFontSize(48)
|
||||
.setType(LabelControl.class)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(800, 60)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 60;
|
||||
|
||||
final var musicEnabled = this.audioControl().getAudioOptions().getMusicOptions().isEnable();
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_Audio_Music_Enable", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-98", "Enable"))
|
||||
.setFontSize(42)
|
||||
.setType(SwitchControl.class)
|
||||
.setControlData(new SwitchData(musicEnabled, false, null, true))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(620, 60)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 80;
|
||||
|
||||
final var musicVolume = this.audioControl().getAudioOptions().getMusicOptions().getVolume();
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_Audio_Label_Music_Volume", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-99", "Volume:"))
|
||||
.setFontSize(42)
|
||||
.setType(LabelControl.class)
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(200, 60)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_Audio_Music_Volume_Slider", ControlRenderingBo.class)
|
||||
.setCaption(String.format("%.2f", musicVolume))
|
||||
.setFontSize(42)
|
||||
.setType(SliderControl.class)
|
||||
.setControlData(new SliderData(true, musicVolume, 0.0, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(musicEnabled)
|
||||
.withDimensionAbsolute(400, 60)
|
||||
.withPositionRelative(220, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(final UiControlEvent event, final OptionsModuleContext context) {
|
||||
final var controlId = event.getId();
|
||||
if (controlId == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (controlId) {
|
||||
case "OptionsModule_Audio_SFX_Enable" -> {
|
||||
final var currentValue = this.audioControl().getAudioOptions().getSfxOptions().isEnable();
|
||||
this.audioControl().getAudioOptions().getSfxOptions().setEnable(!currentValue);
|
||||
this.audioControl().applyAudioOptions(this.audioControl().getAudioOptions());
|
||||
this.refreshContent(context);
|
||||
return true;
|
||||
}
|
||||
case "OptionsModule_Audio_Music_Enable" -> {
|
||||
final var currentValue = this.audioControl().getAudioOptions().getMusicOptions().isEnable();
|
||||
this.audioControl().getAudioOptions().getMusicOptions().setEnable(!currentValue);
|
||||
this.audioControl().applyAudioOptions(this.audioControl().getAudioOptions());
|
||||
this.refreshContent(context);
|
||||
return true;
|
||||
}
|
||||
case "OptionsModule_Audio_SFX_SoftwareMixing" -> {
|
||||
final var currentValue = this.audioControl().getAudioOptions().getSfxOptions().isSoftwareMixing();
|
||||
this.audioControl().getAudioOptions().getSfxOptions().setSoftwareMixing(!currentValue);
|
||||
this.audioControl().applyAudioOptions(this.audioControl().getAudioOptions());
|
||||
this.refreshContent(context);
|
||||
return true;
|
||||
}
|
||||
case "OptionsModule_Audio_Music_SoftwareMixing" -> {
|
||||
final var currentValue = this.audioControl().getAudioOptions().getMusicOptions().isSoftwareMixing();
|
||||
this.audioControl().getAudioOptions().getMusicOptions().setSoftwareMixing(!currentValue);
|
||||
this.audioControl().applyAudioOptions(this.audioControl().getAudioOptions());
|
||||
this.refreshContent(context);
|
||||
return true;
|
||||
}
|
||||
default -> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package de.extio.game_engine.menu;
|
||||
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.LabelControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.LabelData;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.TextfieldControl;
|
||||
import de.extio.game_engine.renderer.model.bo.HorizontalAlignment;
|
||||
import de.extio.game_engine.renderer.model.event.UiControlEvent;
|
||||
|
||||
final class OptionsKeyboardTab extends AbstractOptionsTab implements OptionsTab {
|
||||
|
||||
static final String TAB_ID = "keyboard";
|
||||
private static final String CONTROL_ID = "OptionsModule_Tab_Keyboard";
|
||||
|
||||
@Override
|
||||
public String tabId() {
|
||||
return TAB_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String controlId() {
|
||||
return CONTROL_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String navigationLocalizationKey() {
|
||||
return "ecyoa-86";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fallbackCaption() {
|
||||
return "Keyboard";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int navigationOrder() {
|
||||
return 50;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visibleInNavigation() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(final OptionsModuleContext context) {
|
||||
final var keycodeRegistrations = this.keycodeRegistry().getAll();
|
||||
|
||||
var yPos = 10;
|
||||
|
||||
var bo = this.renderingBoPool().acquire("OptionsModule_Keyboard_Label_Header", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-102", "Keyboard Shortcuts:"))
|
||||
.setFontSize(42)
|
||||
.setType(LabelControl.class)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(800, 80)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 90;
|
||||
|
||||
for (final var registration : keycodeRegistrations) {
|
||||
final var controlId = "OptionsModule_Keyboard_" + sanitizeId(registration.getQualifier());
|
||||
|
||||
bo = this.renderingBoPool().acquire(controlId + "_Label", ControlRenderingBo.class)
|
||||
.setCaption(registration.getQualifier())
|
||||
.setFontSize(42)
|
||||
.setType(LabelControl.class)
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(400, 60)
|
||||
.withPositionRelative(10, yPos + 10);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
bo = this.renderingBoPool().acquire(controlId + "_Key", ControlRenderingBo.class)
|
||||
.setCaption(registration.getKeyCodeDisplay() != null ? registration.getKeyCodeDisplay() : String.valueOf((char) registration.getCode()))
|
||||
.setFontSize(42)
|
||||
.setType(TextfieldControl.class)
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(400, 60)
|
||||
.withPositionRelative(420, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 70;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(final UiControlEvent event, final OptionsModuleContext context) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
280
src/main/java/de/extio/game_engine/menu/OptionsModule.java
Normal file
280
src/main/java/de/extio/game_engine/menu/OptionsModule.java
Normal file
@@ -0,0 +1,280 @@
|
||||
package de.extio.game_engine.menu;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import de.extio.game_engine.event.EventService;
|
||||
import de.extio.game_engine.i18n.LocalizationService;
|
||||
import de.extio.game_engine.module.AbstractClientModule;
|
||||
import de.extio.game_engine.renderer.RendererControl;
|
||||
import de.extio.game_engine.renderer.container.ScrollArea;
|
||||
import de.extio.game_engine.renderer.container.Window;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.ButtonControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.ToggleButtonControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.ToggleButtonData;
|
||||
import de.extio.game_engine.renderer.model.event.MouseClickEvent;
|
||||
import de.extio.game_engine.renderer.model.event.UiControlEvent;
|
||||
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
||||
import de.extio.game_engine.spatial2.model.CoordI2;
|
||||
import de.extio.game_engine.spatial2.model.ImmutableCoordI2;
|
||||
|
||||
public class OptionsModule extends AbstractClientModule implements OptionsModuleContext {
|
||||
|
||||
private static final String TAB_GROUP = "OptionsModule_TabGroup";
|
||||
private static final int TAB_BUTTON_HEIGHT = 60;
|
||||
private static final int TAB_BUTTON_WIDTH = 220;
|
||||
private static final int CONTENT_START_Y = 140;
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final RenderingBoPool renderingBoPool;
|
||||
|
||||
private final EventService eventService;
|
||||
|
||||
private final LocalizationService localizationService;
|
||||
|
||||
@Autowired
|
||||
private List<OptionsTab> discoveredTabs;
|
||||
|
||||
OptionsModule(final ApplicationContext applicationContext, final RenderingBoPool renderingBoPool, final EventService eventService,
|
||||
final LocalizationService localizationService) {
|
||||
this.applicationContext = applicationContext;
|
||||
this.renderingBoPool = renderingBoPool;
|
||||
this.eventService = eventService;
|
||||
this.localizationService = localizationService;
|
||||
}
|
||||
|
||||
private List<OptionsTab> tabs = List.of();
|
||||
private Map<String, OptionsTab> tabsById = Map.of();
|
||||
private Map<String, OptionsTab> tabsByControlId = Map.of();
|
||||
|
||||
private Window optionsWindow;
|
||||
private ScrollArea contentScrollArea;
|
||||
private String activeTabId;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.initializeTabs();
|
||||
this.optionsWindow = this.applicationContext.getBean(Window.class);
|
||||
this.optionsWindow.setNormalizedDimension(RendererControl.REFERENCE_RESOLUTION.divide(9).multiply(7));
|
||||
this.optionsWindow.setNormalizedPosition(centeredPosition(this.optionsWindow.getNormalizedDimension()));
|
||||
this.optionsWindow.setDraggable(false);
|
||||
this.optionsWindow.setCloseButton(true);
|
||||
this.optionsWindow.setOnCloseAction(this::closeOptions);
|
||||
|
||||
this.contentScrollArea = this.applicationContext.getBean(ScrollArea.class);
|
||||
this.optionsWindow.addComponent(this.contentScrollArea);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnload() {
|
||||
this.getModuleService().unloadModule(this.optionsWindow.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivate() {
|
||||
this.setupOptionsWindow();
|
||||
this.getModuleService().changeActiveState(this.optionsWindow.getId(), true);
|
||||
this.getModuleService().hideExcept(this.getId(), this.optionsWindow.getId());
|
||||
this.eventService.register(UiControlEvent.class, this.getId(), this::onUiControlEvent);
|
||||
this.eventService.register(OptionsThemeEditorSavedEvent.class, this.getId(), this::onThemeEditorSaved);
|
||||
this.eventService.register(MouseClickEvent.class, this.getId(), event -> {
|
||||
if (event.isPressed() && (event.getButton() == 1 || event.getButton() == 3)) {
|
||||
this.refreshContent();
|
||||
}
|
||||
});
|
||||
this.eventService.register(OpenOptionsModuleEvent.class, this.getId(), event -> {
|
||||
if (event.tab() != null && !event.tab().isBlank()) {
|
||||
this.openTab(event.tab());
|
||||
} else {
|
||||
this.getModuleService().changeActiveState(this.getId(), true);
|
||||
this.getModuleService().changeDisplayState(this.getId(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeactivate() {
|
||||
this.eventService.unregisterAll(this.getId());
|
||||
this.getModuleService().changeActiveState(this.optionsWindow.getId(), false);
|
||||
this.getModuleService().restoreVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShow() {
|
||||
this.getModuleService().changeDisplayState(this.optionsWindow.getId(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHide() {
|
||||
this.getModuleService().changeDisplayState(this.optionsWindow.getId(), false);
|
||||
}
|
||||
|
||||
public void openTab(final String tabId) {
|
||||
this.initializeTabs();
|
||||
if (this.tabsById.containsKey(tabId)) {
|
||||
this.activeTabId = tabId;
|
||||
}
|
||||
this.getModuleService().changeActiveState(this.getId(), true);
|
||||
this.getModuleService().changeDisplayState(this.getId(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rebuildWindow() {
|
||||
this.setupOptionsWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshContent() {
|
||||
for (final var boId : this.contentScrollArea.getRenderingBoIds().toArray(new String[0])) {
|
||||
this.contentScrollArea.removeRenderingBo(boId);
|
||||
}
|
||||
for (final var tab : this.tabs) {
|
||||
tab.onContentCleared(this);
|
||||
}
|
||||
this.activeTab().render(this);
|
||||
this.optionsWindow.draw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Window optionsWindow() {
|
||||
return this.optionsWindow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScrollArea contentScrollArea() {
|
||||
return this.contentScrollArea;
|
||||
}
|
||||
|
||||
private void setupOptionsWindow() {
|
||||
final var windowWidth = this.optionsWindow.getNormalizedDimension().getX();
|
||||
|
||||
final var tabsY = Window.MARGIN_TOP;
|
||||
final var tabSpacing = 10;
|
||||
var tabX = Window.MARGIN_LEFT;
|
||||
|
||||
for (final var tab : this.tabs) {
|
||||
if (!tab.visibleInNavigation()) {
|
||||
continue;
|
||||
}
|
||||
final var bo = this.renderingBoPool.acquire(tab.controlId(), ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService.translate(tab.navigationLocalizationKey(), tab.fallbackCaption()))
|
||||
.setFontSize(36)
|
||||
.setType(ToggleButtonControl.class)
|
||||
.setControlGroup(TAB_GROUP)
|
||||
.setControlData(new ToggleButtonData(tab.tabId().equals(this.activeTabId), false, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT)
|
||||
.withPositionRelative(tabX, tabsY);
|
||||
this.optionsWindow.putRenderingBo(bo);
|
||||
tabX += TAB_BUTTON_WIDTH + tabSpacing;
|
||||
}
|
||||
|
||||
final var bottomButtonsY = this.optionsWindow.getNormalizedDimension().getY() - Window.MARGIN_BOTTOM - 60;
|
||||
final var buttonWidth = 200;
|
||||
final var buttonHeight = 60;
|
||||
|
||||
final var closeButton = this.renderingBoPool.acquire("OptionsModule_Button_Close", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService.translate("ecyoa-67", "Cancel"))
|
||||
.setFontSize(36)
|
||||
.setType(ButtonControl.class)
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(buttonWidth, buttonHeight)
|
||||
.withPositionRelative(windowWidth - Window.MARGIN_RIGHT - buttonWidth, bottomButtonsY);
|
||||
this.optionsWindow.putRenderingBo(closeButton);
|
||||
|
||||
this.contentScrollArea.setRelativePosition(ImmutableCoordI2.create(Window.MARGIN_LEFT, CONTENT_START_Y));
|
||||
this.contentScrollArea.setDimension(ImmutableCoordI2.create(
|
||||
windowWidth - Window.MARGIN_LEFT - Window.MARGIN_RIGHT,
|
||||
bottomButtonsY - CONTENT_START_Y - 20));
|
||||
|
||||
this.refreshContent();
|
||||
this.optionsWindow.draw();
|
||||
}
|
||||
|
||||
private void initializeTabs() {
|
||||
if (!this.tabs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final var orderedTabs = new ArrayList<>(this.discoveredTabs);
|
||||
orderedTabs.sort(Comparator.comparingInt(OptionsTab::navigationOrder));
|
||||
this.tabs = List.copyOf(orderedTabs);
|
||||
|
||||
final var tabsById = new LinkedHashMap<String, OptionsTab>();
|
||||
final var tabsByControlId = new LinkedHashMap<String, OptionsTab>();
|
||||
for (final var tab : this.tabs) {
|
||||
if (tabsById.put(tab.tabId(), tab) != null) {
|
||||
throw new IllegalStateException("Duplicate options tab id: " + tab.tabId());
|
||||
}
|
||||
if (tabsByControlId.put(tab.controlId(), tab) != null) {
|
||||
throw new IllegalStateException("Duplicate options tab control id: " + tab.controlId());
|
||||
}
|
||||
}
|
||||
this.tabsById = Map.copyOf(tabsById);
|
||||
this.tabsByControlId = Map.copyOf(tabsByControlId);
|
||||
|
||||
if (this.activeTabId == null && !this.tabs.isEmpty()) {
|
||||
this.activeTabId = this.tabs.stream()
|
||||
.filter(OptionsTab::visibleInNavigation)
|
||||
.findFirst()
|
||||
.orElse(this.tabs.getFirst())
|
||||
.tabId();
|
||||
}
|
||||
}
|
||||
|
||||
private OptionsTab activeTab() {
|
||||
this.initializeTabs();
|
||||
final var activeTab = this.tabsById.get(this.activeTabId);
|
||||
if (activeTab != null) {
|
||||
return activeTab;
|
||||
}
|
||||
return this.tabs.getFirst();
|
||||
}
|
||||
|
||||
private void onUiControlEvent(final UiControlEvent event) {
|
||||
final var controlId = event.getId();
|
||||
if (controlId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final var selectedTab = this.tabsByControlId.get(controlId);
|
||||
if (selectedTab != null) {
|
||||
this.activeTabId = selectedTab.tabId();
|
||||
this.setupOptionsWindow();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (controlId) {
|
||||
case "OptionsModule_Button_Close" -> this.closeOptions();
|
||||
default -> this.activeTab().handle(event, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void onThemeEditorSaved(final OptionsThemeEditorSavedEvent event) {
|
||||
if (event.saved()) {
|
||||
this.refreshContent();
|
||||
}
|
||||
}
|
||||
|
||||
private void closeOptions() {
|
||||
this.getModuleService().changeActiveState(this.getId(), false);
|
||||
this.getModuleService().changeDisplayState(this.getId(), false);
|
||||
}
|
||||
|
||||
private static ImmutableCoordI2 centeredPosition(final CoordI2 dimension) {
|
||||
final var referenceResolution = RendererControl.REFERENCE_RESOLUTION;
|
||||
final var x = Math.max(0, (referenceResolution.getX() - dimension.getX()) / 2);
|
||||
final var y = Math.max(0, (referenceResolution.getY() - dimension.getY()) / 2);
|
||||
return ImmutableCoordI2.create(x, y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package de.extio.game_engine.menu;
|
||||
|
||||
import de.extio.game_engine.renderer.container.ScrollArea;
|
||||
import de.extio.game_engine.renderer.container.Window;
|
||||
|
||||
public interface OptionsModuleContext {
|
||||
|
||||
Window optionsWindow();
|
||||
|
||||
ScrollArea contentScrollArea();
|
||||
|
||||
void refreshContent();
|
||||
|
||||
void rebuildWindow();
|
||||
|
||||
default int contentWidth() {
|
||||
return this.optionsWindow().getNormalizedDimension().getX() - Window.MARGIN_LEFT - Window.MARGIN_RIGHT;
|
||||
}
|
||||
}
|
||||
26
src/main/java/de/extio/game_engine/menu/OptionsTab.java
Normal file
26
src/main/java/de/extio/game_engine/menu/OptionsTab.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package de.extio.game_engine.menu;
|
||||
|
||||
import de.extio.game_engine.renderer.model.event.UiControlEvent;
|
||||
|
||||
public interface OptionsTab {
|
||||
String tabId();
|
||||
|
||||
String controlId();
|
||||
|
||||
String navigationLocalizationKey();
|
||||
|
||||
String fallbackCaption();
|
||||
|
||||
int navigationOrder();
|
||||
|
||||
default boolean visibleInNavigation() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void render(OptionsModuleContext context);
|
||||
|
||||
boolean handle(UiControlEvent event, OptionsModuleContext context);
|
||||
|
||||
default void onContentCleared(final OptionsModuleContext context) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package de.extio.game_engine.menu;
|
||||
|
||||
import de.extio.game_engine.event.Event;
|
||||
import de.extio.game_engine.renderer.model.Theme;
|
||||
|
||||
public record OptionsThemeEditorSavedEvent(boolean saved, Theme theme) implements Event {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package de.extio.game_engine.menu;
|
||||
|
||||
import de.extio.game_engine.renderer.container.Window;
|
||||
import de.extio.game_engine.renderer.model.Theme;
|
||||
|
||||
public interface OptionsThemeEditorSupport {
|
||||
|
||||
void openNew(Theme baseTheme, String proposedName, Window parentWindow);
|
||||
|
||||
void openEdit(Theme theme, Window parentWindow);
|
||||
}
|
||||
387
src/main/java/de/extio/game_engine/menu/OptionsThemesTab.java
Normal file
387
src/main/java/de/extio/game_engine/menu/OptionsThemesTab.java
Normal file
@@ -0,0 +1,387 @@
|
||||
package de.extio.game_engine.menu;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import de.extio.game_engine.renderer.model.RenderingBoHasDimension;
|
||||
import de.extio.game_engine.renderer.model.RenderingBoLayer;
|
||||
import de.extio.game_engine.renderer.model.Theme;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.ButtonControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.LabelControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.LabelData;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.PopupMenuControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.PopupMenuData;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.PopupMenuItem;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.ToggleButtonControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.ToggleButtonData;
|
||||
import de.extio.game_engine.renderer.model.bo.HorizontalAlignment;
|
||||
import de.extio.game_engine.renderer.model.event.UiControlEvent;
|
||||
|
||||
final class OptionsThemesTab extends AbstractOptionsTab implements OptionsTab {
|
||||
|
||||
static final String TAB_ID = "themes";
|
||||
private static final String CONTROL_ID = "OptionsModule_Tab_Themes";
|
||||
|
||||
private static final List<String> CUSTOM_THEME_PATH = List.of("customThemes");
|
||||
private static final String CUSTOM_THEME_MENU_ID = "OptionsModule_CustomTheme_Menu";
|
||||
private static final String CUSTOM_THEME_MENU_SELECT = "OptionsModule_CustomThemeMenu_Select";
|
||||
private static final String CUSTOM_THEME_MENU_EDIT = "OptionsModule_CustomThemeMenu_Edit";
|
||||
private static final String CUSTOM_THEME_MENU_DELETE = "OptionsModule_CustomThemeMenu_Delete";
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<OptionsThemeEditorSupport> themeEditorSupportProvider;
|
||||
|
||||
private final Map<String, String> themeSelectionByControlId = new HashMap<>();
|
||||
private final Map<String, String> customThemeSelectionByControlId = new HashMap<>();
|
||||
private String activeCustomThemeName;
|
||||
|
||||
@Override
|
||||
public String tabId() {
|
||||
return TAB_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String controlId() {
|
||||
return CONTROL_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String navigationLocalizationKey() {
|
||||
return "ecyoa-87";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fallbackCaption() {
|
||||
return "Themes";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int navigationOrder() {
|
||||
return 20;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(final OptionsModuleContext context) {
|
||||
this.themeSelectionByControlId.clear();
|
||||
this.customThemeSelectionByControlId.clear();
|
||||
|
||||
final var themeEditorSupport = this.themeEditorSupportProvider.getIfAvailable();
|
||||
final var customThemeNames = this.getCustomThemeNames();
|
||||
final var themeNames = this.applicationContext().getBeansOfType(Theme.class).values().stream()
|
||||
.map(Theme::getName)
|
||||
.sorted()
|
||||
.toList();
|
||||
final var currentTheme = this.themeManager().getCurrentTheme();
|
||||
final var currentThemeName = currentTheme != null ? currentTheme.getName() : null;
|
||||
|
||||
var yPos = 10;
|
||||
|
||||
var bo = this.renderingBoPool().acquire("OptionsModule_Themes_Label_Header", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-103", "Available Themes:"))
|
||||
.setFontSize(42)
|
||||
.setType(LabelControl.class)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(800, 80)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 90;
|
||||
|
||||
final var columnWidth = 330;
|
||||
final var columnSpacing = 20;
|
||||
var xPos = 10;
|
||||
var columnIndex = 0;
|
||||
var emptyRow = false;
|
||||
|
||||
for (final var themeName : themeNames) {
|
||||
emptyRow = false;
|
||||
final var controlId = "OptionsModule_Theme_" + sanitizeId(themeName);
|
||||
this.themeSelectionByControlId.put(controlId, themeName);
|
||||
|
||||
bo = this.renderingBoPool().acquire(controlId, ControlRenderingBo.class)
|
||||
.setCaption(themeName)
|
||||
.setFontSize(32)
|
||||
.setType(ToggleButtonControl.class)
|
||||
.setControlGroup("OptionsModule_ThemeGroup")
|
||||
.setControlData(new ToggleButtonData(themeName.equals(currentThemeName), true, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(columnWidth, 60)
|
||||
.withPositionRelative(xPos, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
columnIndex++;
|
||||
if (columnIndex >= 4) {
|
||||
columnIndex = 0;
|
||||
xPos = 10;
|
||||
yPos += 70;
|
||||
emptyRow = true;
|
||||
}
|
||||
else {
|
||||
xPos += columnWidth + columnSpacing;
|
||||
}
|
||||
}
|
||||
|
||||
yPos += emptyRow ? 20 : 90;
|
||||
xPos = 10;
|
||||
columnIndex = 0;
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_Themes_Label_Custom", ControlRenderingBo.class)
|
||||
.setCaption("Custom Themes")
|
||||
.setFontSize(40)
|
||||
.setType(LabelControl.class)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(600, 70)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 90;
|
||||
|
||||
for (final var themeName : customThemeNames) {
|
||||
final var controlId = "OptionsModule_CustomTheme_" + sanitizeId(themeName);
|
||||
this.customThemeSelectionByControlId.put(controlId, themeName);
|
||||
|
||||
bo = this.renderingBoPool().acquire(controlId, ControlRenderingBo.class)
|
||||
.setCaption(themeName)
|
||||
.setFontSize(32)
|
||||
.setType(ToggleButtonControl.class)
|
||||
.setControlGroup("OptionsModule_CustomThemeGroup")
|
||||
.setControlData(new ToggleButtonData(themeName.equals(currentThemeName), true, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(columnWidth, 60)
|
||||
.withPositionRelative(xPos, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
columnIndex++;
|
||||
if (columnIndex >= 4) {
|
||||
columnIndex = 0;
|
||||
xPos = 10;
|
||||
yPos += 70;
|
||||
}
|
||||
else {
|
||||
xPos += columnWidth + columnSpacing;
|
||||
}
|
||||
}
|
||||
|
||||
if (themeEditorSupport != null) {
|
||||
final var addBo = this.renderingBoPool().acquire("OptionsModule_CustomTheme_Add", ControlRenderingBo.class)
|
||||
.setCaption("+")
|
||||
.setFontSize(36)
|
||||
.setType(ButtonControl.class)
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(columnWidth, 60)
|
||||
.withPositionRelative(xPos, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(addBo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(final UiControlEvent event, final OptionsModuleContext context) {
|
||||
final var controlId = event.getId();
|
||||
if (controlId == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (controlId) {
|
||||
case "OptionsModule_CustomTheme_Add" -> {
|
||||
this.openNewThemeEditor(context);
|
||||
return true;
|
||||
}
|
||||
case CUSTOM_THEME_MENU_ID -> {
|
||||
this.handleCustomThemeMenuSelection(event.getPayload(), context);
|
||||
return true;
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
|
||||
final var customThemeName = this.customThemeSelectionByControlId.get(controlId);
|
||||
if (customThemeName != null) {
|
||||
final var bo = this.optionsWindow(context).getRenderingBo(controlId, ControlRenderingBo.class);
|
||||
if (bo != null) {
|
||||
this.showCustomThemeMenu(bo, customThemeName, context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
final var themeName = this.themeSelectionByControlId.get(controlId);
|
||||
if (themeName != null) {
|
||||
this.themeManager().setCurrentTheme(themeName);
|
||||
this.refreshContent(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContentCleared(final OptionsModuleContext context) {
|
||||
this.hideCustomThemeMenu(context);
|
||||
this.themeSelectionByControlId.clear();
|
||||
this.customThemeSelectionByControlId.clear();
|
||||
}
|
||||
|
||||
private List<String> getCustomThemeNames() {
|
||||
final var descriptors = this.storageService().listPath(CUSTOM_THEME_PATH, false);
|
||||
final var names = new ArrayList<String>(descriptors.size());
|
||||
for (final var descriptor : descriptors) {
|
||||
if (descriptor != null && descriptor.name() != null && !descriptor.name().isBlank()) {
|
||||
names.add(descriptor.name());
|
||||
}
|
||||
}
|
||||
names.sort(String::compareToIgnoreCase);
|
||||
return List.copyOf(names);
|
||||
}
|
||||
|
||||
private void showCustomThemeMenu(final ControlRenderingBo bo, final String themeName, final OptionsModuleContext context) {
|
||||
if (themeName.equals(this.activeCustomThemeName)
|
||||
&& this.optionsWindow(context).getRenderingBo(CUSTOM_THEME_MENU_ID, ControlRenderingBo.class) != null) {
|
||||
this.hideCustomThemeMenu(context);
|
||||
final var controlId = "OptionsModule_CustomTheme_" + sanitizeId(themeName);
|
||||
final var control = this.optionsWindow(context).getRenderingBo(controlId, ControlRenderingBo.class);
|
||||
if (control != null) {
|
||||
final var currentTheme = this.themeManager().getCurrentTheme();
|
||||
control.setControlData(new ToggleButtonData(currentTheme != null && themeName.equals(currentTheme.getName()), true, null));
|
||||
this.optionsWindow(context).putRenderingBo(control);
|
||||
}
|
||||
this.optionsWindow(context).draw();
|
||||
return;
|
||||
}
|
||||
|
||||
this.hideCustomThemeMenu(context);
|
||||
this.activeCustomThemeName = themeName;
|
||||
|
||||
if (!(bo instanceof final RenderingBoHasDimension boDim)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final var items = new ArrayList<PopupMenuItem>();
|
||||
items.add(new PopupMenuItem(CUSTOM_THEME_MENU_SELECT, "Select", true));
|
||||
if (this.themeEditorSupportProvider.getIfAvailable() != null) {
|
||||
items.add(new PopupMenuItem(CUSTOM_THEME_MENU_EDIT, "Edit", true));
|
||||
}
|
||||
items.add(new PopupMenuItem(CUSTOM_THEME_MENU_DELETE, "Delete", true));
|
||||
|
||||
final var rowHeight = 32;
|
||||
final var padding = 8;
|
||||
final var width = Math.min(240, boDim.getWidth());
|
||||
final var height = (rowHeight * items.size()) + (padding * 2);
|
||||
final var offset = this.contentScrollArea(context).getRelativePosition();
|
||||
final var x = offset.getX() + bo.getLocalX();
|
||||
final var y = offset.getY() + bo.getLocalY() + boDim.getHeight() + 6;
|
||||
|
||||
final var data = new PopupMenuData(List.copyOf(items), null, null, null, rowHeight, padding);
|
||||
final var popup = this.renderingBoPool().acquire(CUSTOM_THEME_MENU_ID, ControlRenderingBo.class)
|
||||
.setType(PopupMenuControl.class)
|
||||
.setFontSize(22)
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.setControlData(data)
|
||||
.withDimensionAbsolute(width, height)
|
||||
.setLayer(RenderingBoLayer.UI1)
|
||||
.withPositionRelative(x, y);
|
||||
this.optionsWindow(context).putRenderingBo(popup);
|
||||
this.optionsWindow(context).draw();
|
||||
}
|
||||
|
||||
private void handleCustomThemeMenuSelection(final Object payload, final OptionsModuleContext context) {
|
||||
if (this.activeCustomThemeName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String actionId;
|
||||
if (payload instanceof final String id) {
|
||||
actionId = id;
|
||||
}
|
||||
else if (payload instanceof final PopupMenuItem item) {
|
||||
actionId = item.id();
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (actionId) {
|
||||
case CUSTOM_THEME_MENU_SELECT -> this.applyCustomTheme(this.activeCustomThemeName, context);
|
||||
case CUSTOM_THEME_MENU_EDIT -> this.editCustomTheme(this.activeCustomThemeName, context);
|
||||
case CUSTOM_THEME_MENU_DELETE -> this.deleteCustomTheme(this.activeCustomThemeName, context);
|
||||
default -> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.refreshContent(context);
|
||||
}
|
||||
|
||||
private void hideCustomThemeMenu(final OptionsModuleContext context) {
|
||||
this.optionsWindow(context).removeRenderingBo(CUSTOM_THEME_MENU_ID);
|
||||
this.activeCustomThemeName = null;
|
||||
}
|
||||
|
||||
private void applyCustomTheme(final String themeName, final OptionsModuleContext context) {
|
||||
final var theme = this.loadCustomTheme(themeName);
|
||||
if (theme == null) {
|
||||
return;
|
||||
}
|
||||
this.themeManager().setCurrentTheme(theme);
|
||||
this.refreshContent(context);
|
||||
}
|
||||
|
||||
private void editCustomTheme(final String themeName, final OptionsModuleContext context) {
|
||||
final var themeEditorSupport = this.themeEditorSupportProvider.getIfAvailable();
|
||||
if (themeEditorSupport == null) {
|
||||
return;
|
||||
}
|
||||
final var theme = this.loadCustomTheme(themeName);
|
||||
if (theme == null) {
|
||||
return;
|
||||
}
|
||||
themeEditorSupport.openEdit(theme, this.optionsWindow(context));
|
||||
}
|
||||
|
||||
private void deleteCustomTheme(final String themeName, final OptionsModuleContext context) {
|
||||
this.storageService().deleteByPath(CUSTOM_THEME_PATH, themeName);
|
||||
final var currentTheme = this.themeManager().getCurrentTheme();
|
||||
if (currentTheme != null && themeName.equalsIgnoreCase(currentTheme.getName())) {
|
||||
this.themeManager().setCurrentTheme((String) null);
|
||||
}
|
||||
this.refreshContent(context);
|
||||
}
|
||||
|
||||
private Theme loadCustomTheme(final String themeName) {
|
||||
return this.storageService().loadByPath(Theme.class, CUSTOM_THEME_PATH, themeName).orElse(null);
|
||||
}
|
||||
|
||||
private void openNewThemeEditor(final OptionsModuleContext context) {
|
||||
final var themeEditorSupport = this.themeEditorSupportProvider.getIfAvailable();
|
||||
if (themeEditorSupport == null) {
|
||||
return;
|
||||
}
|
||||
final var name = this.generateNewThemeName();
|
||||
themeEditorSupport.openNew(this.themeManager().getCurrentTheme(), name, this.optionsWindow(context));
|
||||
}
|
||||
|
||||
private String generateNewThemeName() {
|
||||
final var existing = this.getCustomThemeNames();
|
||||
final var base = "Custom Theme";
|
||||
if (!existing.contains(base)) {
|
||||
return base;
|
||||
}
|
||||
var index = 2;
|
||||
while (existing.contains(base + " " + index)) {
|
||||
index++;
|
||||
}
|
||||
return base + " " + index;
|
||||
}
|
||||
}
|
||||
287
src/main/java/de/extio/game_engine/menu/OptionsVideoTab.java
Normal file
287
src/main/java/de/extio/game_engine/menu/OptionsVideoTab.java
Normal file
@@ -0,0 +1,287 @@
|
||||
package de.extio.game_engine.menu;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.LabelControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.LabelData;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.ToggleButtonControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.ToggleButtonData;
|
||||
import de.extio.game_engine.renderer.model.bo.HorizontalAlignment;
|
||||
import de.extio.game_engine.renderer.model.event.UiControlEvent;
|
||||
import de.extio.game_engine.renderer.model.options.VideoOptions.VideoOptionsVideoMode;
|
||||
|
||||
final class OptionsVideoTab extends AbstractOptionsTab implements OptionsTab {
|
||||
|
||||
static final String TAB_ID = "video";
|
||||
private static final String CONTROL_ID = "OptionsModule_Tab_Video";
|
||||
|
||||
private final Map<String, Integer> framerateSelectionByControlId = new HashMap<>();
|
||||
private final Map<String, Integer> screenSelectionByControlId = new HashMap<>();
|
||||
private final Map<String, Double> scaleFactorSelectionByControlId = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public String tabId() {
|
||||
return TAB_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String controlId() {
|
||||
return CONTROL_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String navigationLocalizationKey() {
|
||||
return "ecyoa-84";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fallbackCaption() {
|
||||
return "Video";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int navigationOrder() {
|
||||
return 30;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(final OptionsModuleContext context) {
|
||||
this.framerateSelectionByControlId.clear();
|
||||
this.screenSelectionByControlId.clear();
|
||||
this.scaleFactorSelectionByControlId.clear();
|
||||
|
||||
var yPos = 10;
|
||||
|
||||
var bo = this.renderingBoPool().acquire("OptionsModule_Video_Label_Mode", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-91", "Video Mode:"))
|
||||
.setFontSize(48)
|
||||
.setType(LabelControl.class)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(800, 80)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 90;
|
||||
|
||||
final var currentMode = this.rendererControl().getVideoOptions().getVideoMode();
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_VideoMode_Window", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-92", "Windowed"))
|
||||
.setFontSize(42)
|
||||
.setType(ToggleButtonControl.class)
|
||||
.setControlGroup("OptionsModule_VideoModeGroup")
|
||||
.setControlData(new ToggleButtonData(currentMode == VideoOptionsVideoMode.WINDOW, false, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(300, 60)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_VideoMode_Borderless", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-93", "Borderless"))
|
||||
.setFontSize(42)
|
||||
.setType(ToggleButtonControl.class)
|
||||
.setControlGroup("OptionsModule_VideoModeGroup")
|
||||
.setControlData(new ToggleButtonData(currentMode == VideoOptionsVideoMode.BORDERLESS, false, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(300, 60)
|
||||
.withPositionRelative(320, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_VideoMode_Fullscreen", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-94", "Fullscreen"))
|
||||
.setFontSize(42)
|
||||
.setType(ToggleButtonControl.class)
|
||||
.setControlGroup("OptionsModule_VideoModeGroup")
|
||||
.setControlData(new ToggleButtonData(currentMode == VideoOptionsVideoMode.FULLSCREEN, false, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(300, 60)
|
||||
.withPositionRelative(630, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 100;
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_Video_Label_Screen", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-105", "Screen:"))
|
||||
.setFontSize(48)
|
||||
.setType(LabelControl.class)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(800, 80)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 90;
|
||||
|
||||
final var screenDevices = this.rendererControl().getScreenDevicesCount();
|
||||
final var currentScreen = this.rendererControl().getVideoOptions().getFullScreenNumber();
|
||||
|
||||
var screenXPos = 10;
|
||||
for (var i = 0; i < screenDevices; i++) {
|
||||
final var controlId = "OptionsModule_Video_Screen_" + i;
|
||||
this.screenSelectionByControlId.put(controlId, i);
|
||||
|
||||
bo = this.renderingBoPool().acquire(controlId, ControlRenderingBo.class)
|
||||
.setCaption(String.valueOf(i + 1))
|
||||
.setFontSize(42)
|
||||
.setType(ToggleButtonControl.class)
|
||||
.setControlGroup("OptionsModule_ScreenGroup")
|
||||
.setControlData(new ToggleButtonData(currentScreen == i, false, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(80, 60)
|
||||
.withPositionRelative(screenXPos, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
screenXPos += 90;
|
||||
}
|
||||
|
||||
yPos += 100;
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_Video_Label_ScaleFactor", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-95", "Scale Factor:"))
|
||||
.setFontSize(48)
|
||||
.setType(LabelControl.class)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(800, 80)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 90;
|
||||
|
||||
final var currentScaleFactor = this.rendererControl().getVideoOptions().getScaleFactorModifier();
|
||||
final var scaleFactors = List.of(0.75, 0.90, 1.00, 1.15, 1.25);
|
||||
|
||||
var scaleXPos = 10;
|
||||
for (final var scaleFactor : scaleFactors) {
|
||||
final var controlId = "OptionsModule_Video_ScaleFactor_" + (int) (scaleFactor * 100);
|
||||
this.scaleFactorSelectionByControlId.put(controlId, scaleFactor);
|
||||
|
||||
bo = this.renderingBoPool().acquire(controlId, ControlRenderingBo.class)
|
||||
.setCaption((int) (scaleFactor * 100) + "%")
|
||||
.setFontSize(42)
|
||||
.setType(ToggleButtonControl.class)
|
||||
.setControlGroup("OptionsModule_ScaleFactorGroup")
|
||||
.setControlData(new ToggleButtonData(Math.abs(currentScaleFactor - scaleFactor) < 0.01, false, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(150, 60)
|
||||
.withPositionRelative(scaleXPos, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
scaleXPos += 160;
|
||||
}
|
||||
|
||||
yPos += 100;
|
||||
|
||||
bo = this.renderingBoPool().acquire("OptionsModule_Video_Label_Framerate", ControlRenderingBo.class)
|
||||
.setCaption(this.localizationService().translate("ecyoa-96", "Target Framerate:"))
|
||||
.setFontSize(48)
|
||||
.setType(LabelControl.class)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(800, 80)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
yPos += 90;
|
||||
|
||||
final var currentFramerate = this.rendererControl().getVideoOptions().getFrameRateTarget();
|
||||
final var framerates = List.of(30, 60, 120, 144, 165, 200);
|
||||
|
||||
var xPos = 10;
|
||||
for (final var framerate : framerates) {
|
||||
final var controlId = "OptionsModule_Video_Framerate_" + framerate;
|
||||
this.framerateSelectionByControlId.put(controlId, framerate);
|
||||
|
||||
bo = this.renderingBoPool().acquire(controlId, ControlRenderingBo.class)
|
||||
.setCaption(framerate + " FPS")
|
||||
.setFontSize(42)
|
||||
.setType(ToggleButtonControl.class)
|
||||
.setControlGroup("OptionsModule_FramerateGroup")
|
||||
.setControlData(new ToggleButtonData(currentFramerate == framerate, false, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(220, 60)
|
||||
.withPositionRelative(xPos, yPos);
|
||||
this.contentScrollArea(context).putRenderingBo(bo);
|
||||
|
||||
xPos += 230;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(final UiControlEvent event, final OptionsModuleContext context) {
|
||||
final var controlId = event.getId();
|
||||
if (controlId == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (controlId) {
|
||||
case "OptionsModule_VideoMode_Window" -> {
|
||||
this.rendererControl().getVideoOptions().setVideoMode(VideoOptionsVideoMode.WINDOW);
|
||||
this.rendererControl().applyVideoOptions();
|
||||
this.refreshContent(context);
|
||||
return true;
|
||||
}
|
||||
case "OptionsModule_VideoMode_Borderless" -> {
|
||||
this.rendererControl().getVideoOptions().setVideoMode(VideoOptionsVideoMode.BORDERLESS);
|
||||
this.rendererControl().applyVideoOptions();
|
||||
this.refreshContent(context);
|
||||
return true;
|
||||
}
|
||||
case "OptionsModule_VideoMode_Fullscreen" -> {
|
||||
this.rendererControl().getVideoOptions().setVideoMode(VideoOptionsVideoMode.FULLSCREEN);
|
||||
this.rendererControl().applyVideoOptions();
|
||||
this.refreshContent(context);
|
||||
return true;
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
|
||||
final var framerate = this.framerateSelectionByControlId.get(controlId);
|
||||
if (framerate != null) {
|
||||
this.rendererControl().getVideoOptions().setFrameRateTarget(framerate);
|
||||
this.rendererControl().setFrameRate(framerate);
|
||||
this.refreshContent(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
final var screenNumber = this.screenSelectionByControlId.get(controlId);
|
||||
if (screenNumber != null) {
|
||||
this.rendererControl().getVideoOptions().setFullScreenNumber(screenNumber);
|
||||
this.rendererControl().applyVideoOptions();
|
||||
this.refreshContent(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
final var scaleFactor = this.scaleFactorSelectionByControlId.get(controlId);
|
||||
if (scaleFactor != null) {
|
||||
this.rendererControl().getVideoOptions().setScaleFactorModifier(scaleFactor);
|
||||
this.rendererControl().setScaleFactorModifier(scaleFactor);
|
||||
this.refreshContent(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContentCleared(final OptionsModuleContext context) {
|
||||
this.framerateSelectionByControlId.clear();
|
||||
this.screenSelectionByControlId.clear();
|
||||
this.scaleFactorSelectionByControlId.clear();
|
||||
}
|
||||
}
|
||||
774
src/main/java/de/extio/game_engine/menu/ThemeEditorModule.java
Normal file
774
src/main/java/de/extio/game_engine/menu/ThemeEditorModule.java
Normal file
@@ -0,0 +1,774 @@
|
||||
package de.extio.game_engine.menu;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import de.extio.game_engine.event.EventService;
|
||||
import de.extio.game_engine.menu.HsbColorPickerModule.HsbColorPickerResponseEvent;
|
||||
import de.extio.game_engine.module.AbstractClientModule;
|
||||
import de.extio.game_engine.renderer.RendererControl;
|
||||
import de.extio.game_engine.renderer.ThemeManager;
|
||||
import de.extio.game_engine.renderer.container.ScrollArea;
|
||||
import de.extio.game_engine.renderer.container.Window;
|
||||
import de.extio.game_engine.renderer.model.RenderingBoHasDimension;
|
||||
import de.extio.game_engine.renderer.model.RenderingBoLayer;
|
||||
import de.extio.game_engine.renderer.model.Theme;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.ButtonControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.LabelControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.LabelData;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.PopupMenuControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.PopupMenuData;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.PopupMenuItem;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.SliderControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.SliderData;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.TextfieldControl;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.TextfieldData;
|
||||
import de.extio.game_engine.renderer.model.bo.DrawEffectRenderingBo;
|
||||
import de.extio.game_engine.renderer.model.bo.DrawEffectRenderingBoEffects;
|
||||
import de.extio.game_engine.renderer.model.bo.DrawFontRenderingBo;
|
||||
import de.extio.game_engine.renderer.model.bo.HorizontalAlignment;
|
||||
import de.extio.game_engine.renderer.model.color.HSBColor;
|
||||
import de.extio.game_engine.renderer.model.color.ImmutableRgbaColor;
|
||||
import de.extio.game_engine.renderer.model.event.MouseClickEvent;
|
||||
import de.extio.game_engine.renderer.model.event.UiControlEvent;
|
||||
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
||||
import de.extio.game_engine.resource.StaticResourceService;
|
||||
import de.extio.game_engine.spatial2.model.ImmutableCoordI2;
|
||||
import de.extio.game_engine.storage.StorageService;
|
||||
|
||||
public class ThemeEditorModule extends AbstractClientModule implements OptionsThemeEditorSupport {
|
||||
|
||||
private static final List<String> CUSTOM_THEME_PATH = List.of("customThemes");
|
||||
|
||||
private static final int WINDOW_WIDTH = 1200;
|
||||
|
||||
private static final int WINDOW_HEIGHT = 820;
|
||||
|
||||
private static final int ROW_HEIGHT = 60;
|
||||
|
||||
private static final int LABEL_WIDTH = 320;
|
||||
|
||||
private static final int CONTROL_WIDTH = 620;
|
||||
|
||||
private static final int VALUE_WIDTH = 120;
|
||||
|
||||
private static final int SPACING = 14;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
private RenderingBoPool renderingBoPool;
|
||||
|
||||
@Autowired
|
||||
private EventService eventService;
|
||||
|
||||
@Autowired
|
||||
private ThemeManager themeManager;
|
||||
|
||||
@Autowired
|
||||
private StorageService storageService;
|
||||
|
||||
@Autowired
|
||||
private StaticResourceService staticResourceService;
|
||||
|
||||
@Autowired
|
||||
private HsbColorPickerModule hsbColorPickerModule;
|
||||
|
||||
private Window editorWindow;
|
||||
|
||||
private ScrollArea scrollArea;
|
||||
|
||||
private Theme originalTheme;
|
||||
|
||||
private String originalName;
|
||||
|
||||
private String name;
|
||||
|
||||
private String patternRendererName;
|
||||
|
||||
private String font;
|
||||
|
||||
private HSBColor borderOuter;
|
||||
|
||||
private HSBColor borderInner;
|
||||
|
||||
private HSBColor borderInnerDisabled;
|
||||
|
||||
private HSBColor backgroundNormal;
|
||||
|
||||
private HSBColor backgroundSelected;
|
||||
|
||||
private HSBColor textNormal;
|
||||
|
||||
private HSBColor textDisabled;
|
||||
|
||||
private HSBColor selectionPrimary;
|
||||
|
||||
private HSBColor selectionSecondary;
|
||||
|
||||
private HSBColor windowBackground;
|
||||
|
||||
private float hoverBrightnessAdjustment;
|
||||
|
||||
private float pressedBrightnessAdjustment;
|
||||
|
||||
private final Map<String, ColorSlot> colorSlotByControlId = new HashMap<>();
|
||||
|
||||
private PopupTarget popupTarget;
|
||||
|
||||
private String pendingColorRequestId;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.editorWindow = this.applicationContext.getBean(Window.class);
|
||||
this.editorWindow.setNormalizedDimension(ImmutableCoordI2.create(WINDOW_WIDTH, WINDOW_HEIGHT));
|
||||
this.editorWindow.setNormalizedPosition(this.centeredPosition(this.editorWindow.getNormalizedDimension()));
|
||||
this.editorWindow.setDraggable(true);
|
||||
this.editorWindow.setCloseButton(true);
|
||||
this.editorWindow.setOnCloseAction(this::onCancel);
|
||||
|
||||
this.scrollArea = this.applicationContext.getBean(ScrollArea.class);
|
||||
this.editorWindow.addComponent(this.scrollArea);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnload() {
|
||||
this.getModuleService().unloadModule(this.editorWindow.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivate() {
|
||||
this.setupEditor();
|
||||
this.getModuleService().changeActiveState(this.editorWindow.getId(), true);
|
||||
this.eventService.register(UiControlEvent.class, this.getId(), this::onUiControlEvent);
|
||||
this.eventService.register(HsbColorPickerResponseEvent.class, this.getId(), this::onColorPickerResponse);
|
||||
this.eventService.register(MouseClickEvent.class, this.getId(), event -> {
|
||||
if (event.isPressed() && (event.getButton() == 1 || event.getButton() == 3) && this.popupTarget != null) {
|
||||
this.hidePopup();
|
||||
this.editorWindow.draw();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeactivate() {
|
||||
this.eventService.unregisterAll(this.getId());
|
||||
this.getModuleService().changeActiveState(this.editorWindow.getId(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShow() {
|
||||
this.getModuleService().changeDisplayState(this.editorWindow.getId(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHide() {
|
||||
this.getModuleService().changeDisplayState(this.editorWindow.getId(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openNew(final Theme baseTheme, final String proposedName, final Window parentWindow) {
|
||||
this.originalTheme = this.themeManager.getCurrentTheme();
|
||||
final var template = baseTheme != null ? baseTheme : this.themeManager.getCurrentTheme();
|
||||
this.originalName = null;
|
||||
this.name = proposedName;
|
||||
this.initializeFromTheme(template);
|
||||
// apply the template theme immediately for preview
|
||||
this.applyCurrentThemePreview();
|
||||
this.editorWindow.setParent(parentWindow);
|
||||
this.getModuleService().changeActiveState(this.getId(), true);
|
||||
this.getModuleService().changeDisplayState(this.getId(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openEdit(final Theme theme, final Window parentWindow) {
|
||||
if (theme == null) {
|
||||
return;
|
||||
}
|
||||
this.originalTheme = this.themeManager.getCurrentTheme();
|
||||
this.originalName = theme.getName();
|
||||
this.name = theme.getName();
|
||||
this.initializeFromTheme(theme);
|
||||
// apply the selected theme immediately for preview
|
||||
this.applyCurrentThemePreview();
|
||||
this.editorWindow.setParent(parentWindow);
|
||||
this.getModuleService().changeActiveState(this.getId(), true);
|
||||
this.getModuleService().changeDisplayState(this.getId(), true);
|
||||
}
|
||||
|
||||
private void initializeFromTheme(final Theme theme) {
|
||||
this.patternRendererName = theme.getPatternRendererName();
|
||||
this.font = theme.getFont();
|
||||
this.borderOuter = theme.getBorderOuter();
|
||||
this.borderInner = theme.getBorderInner();
|
||||
this.borderInnerDisabled = theme.getBorderInnerDisabled();
|
||||
this.backgroundNormal = theme.getBackgroundNormal();
|
||||
this.backgroundSelected = theme.getBackgroundSelected();
|
||||
this.textNormal = theme.getTextNormal();
|
||||
this.textDisabled = theme.getTextDisabled();
|
||||
this.selectionPrimary = theme.getSelectionPrimary();
|
||||
this.selectionSecondary = theme.getSelectionSecondary();
|
||||
this.windowBackground = theme.getWindowBackground();
|
||||
this.hoverBrightnessAdjustment = theme.getHoverBrightnessAdjustment();
|
||||
this.pressedBrightnessAdjustment = theme.getPressedBrightnessAdjustment();
|
||||
}
|
||||
|
||||
private void setupEditor() {
|
||||
this.editorWindow.clearRenderingBos();
|
||||
for (final var boId : this.scrollArea.getRenderingBoIds().toArray(new String[0])) {
|
||||
this.scrollArea.removeRenderingBo(boId);
|
||||
}
|
||||
this.colorSlotByControlId.clear();
|
||||
this.popupTarget = null;
|
||||
this.pendingColorRequestId = null;
|
||||
|
||||
final var contentWidth = WINDOW_WIDTH - Window.MARGIN_LEFT - Window.MARGIN_RIGHT;
|
||||
final var bottomButtonsY = WINDOW_HEIGHT - Window.MARGIN_BOTTOM - 60;
|
||||
|
||||
this.scrollArea.setRelativePosition(ImmutableCoordI2.create(Window.MARGIN_LEFT, Window.MARGIN_TOP));
|
||||
this.scrollArea.setDimension(ImmutableCoordI2.create(contentWidth, bottomButtonsY - Window.MARGIN_TOP - SPACING));
|
||||
|
||||
var yPos = 10;
|
||||
|
||||
var bo = this.renderingBoPool.acquire("ThemeEditor_Label_Header", DrawFontRenderingBo.class)
|
||||
.setText("Theme Editor")
|
||||
.setSize(36)
|
||||
.setAlignment(HorizontalAlignment.LEFT)
|
||||
.withDimensionAbsolute(600, 60)
|
||||
.withPositionRelative(10, yPos);
|
||||
this.scrollArea.putRenderingBo(bo);
|
||||
|
||||
yPos += 80;
|
||||
|
||||
yPos = this.addTextFieldRow("Name", "ThemeEditor_Field_Name", this.name, yPos);
|
||||
yPos = this.addPopupRow("Pattern Renderer", "ThemeEditor_Field_Pattern", this.patternRendererName, yPos);
|
||||
yPos = this.addPopupRow("Font", "ThemeEditor_Field_Font", this.font, yPos);
|
||||
yPos = this.addSliderRow("Hover Brightness", "ThemeEditor_Field_Hover", this.hoverBrightnessAdjustment, yPos);
|
||||
yPos = this.addSliderRow("Pressed Brightness", "ThemeEditor_Field_Pressed", this.pressedBrightnessAdjustment, yPos);
|
||||
|
||||
yPos += 20;
|
||||
|
||||
yPos = this.addColorRow("Border Outer", "ThemeEditor_Color_BorderOuter", this.borderOuter, yPos);
|
||||
yPos = this.addColorRow("Border Inner", "ThemeEditor_Color_BorderInner", this.borderInner, yPos);
|
||||
yPos = this.addColorRow("Border Inner Disabled", "ThemeEditor_Color_BorderInnerDisabled", this.borderInnerDisabled, yPos);
|
||||
yPos = this.addColorRow("Background Normal", "ThemeEditor_Color_BackgroundNormal", this.backgroundNormal, yPos);
|
||||
yPos = this.addColorRow("Background Selected", "ThemeEditor_Color_BackgroundSelected", this.backgroundSelected, yPos);
|
||||
yPos = this.addColorRow("Text Normal", "ThemeEditor_Color_TextNormal", this.textNormal, yPos);
|
||||
yPos = this.addColorRow("Text Disabled", "ThemeEditor_Color_TextDisabled", this.textDisabled, yPos);
|
||||
yPos = this.addColorRow("Selection Primary", "ThemeEditor_Color_SelectionPrimary", this.selectionPrimary, yPos);
|
||||
yPos = this.addColorRow("Selection Secondary", "ThemeEditor_Color_SelectionSecondary", this.selectionSecondary, yPos);
|
||||
yPos = this.addColorRow("Window Background", "ThemeEditor_Color_WindowBackground", this.windowBackground, yPos);
|
||||
|
||||
final var buttonWidth = 200;
|
||||
final var buttonHeight = 60;
|
||||
|
||||
bo = this.renderingBoPool.acquire("ThemeEditor_Button_Save", ControlRenderingBo.class)
|
||||
.setCaption("Save")
|
||||
.setFontSize(32)
|
||||
.setType(ButtonControl.class)
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(buttonWidth, buttonHeight)
|
||||
.withPositionRelative(WINDOW_WIDTH - Window.MARGIN_RIGHT - buttonWidth * 2 - SPACING, bottomButtonsY);
|
||||
this.editorWindow.putRenderingBo(bo);
|
||||
|
||||
bo = this.renderingBoPool.acquire("ThemeEditor_Button_Cancel", ControlRenderingBo.class)
|
||||
.setCaption("Cancel")
|
||||
.setFontSize(32)
|
||||
.setType(ButtonControl.class)
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(buttonWidth, buttonHeight)
|
||||
.withPositionRelative(WINDOW_WIDTH - Window.MARGIN_RIGHT - buttonWidth, bottomButtonsY);
|
||||
this.editorWindow.putRenderingBo(bo);
|
||||
|
||||
this.editorWindow.draw();
|
||||
}
|
||||
|
||||
private int addTextFieldRow(final String label, final String fieldId, final String value, final int yPos) {
|
||||
var bo = this.renderingBoPool.acquire("ThemeEditor_Label_" + fieldId, ControlRenderingBo.class)
|
||||
.setCaption(label)
|
||||
.setFontSize(30)
|
||||
.setType(LabelControl.class)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(LABEL_WIDTH, ROW_HEIGHT)
|
||||
.withPositionRelative(10, yPos + 10);
|
||||
this.scrollArea.putRenderingBo(bo);
|
||||
|
||||
bo = this.renderingBoPool.acquire(fieldId, ControlRenderingBo.class)
|
||||
.setCaption(value != null ? value : "")
|
||||
.setFontSize(28)
|
||||
.setType(TextfieldControl.class)
|
||||
.setControlData(new TextfieldData(false, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(CONTROL_WIDTH, ROW_HEIGHT)
|
||||
.withPositionRelative(10 + LABEL_WIDTH + SPACING, yPos);
|
||||
this.scrollArea.putRenderingBo(bo);
|
||||
|
||||
return yPos + ROW_HEIGHT + SPACING;
|
||||
}
|
||||
|
||||
private int addPopupRow(final String label, final String fieldId, final String value, final int yPos) {
|
||||
var bo = this.renderingBoPool.acquire("ThemeEditor_Label_" + fieldId, ControlRenderingBo.class)
|
||||
.setCaption(label)
|
||||
.setFontSize(30)
|
||||
.setType(LabelControl.class)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(LABEL_WIDTH, ROW_HEIGHT)
|
||||
.withPositionRelative(10, yPos + 10);
|
||||
this.scrollArea.putRenderingBo(bo);
|
||||
|
||||
bo = this.renderingBoPool.acquire(fieldId, ControlRenderingBo.class)
|
||||
.setCaption(value != null ? value : "")
|
||||
.setFontSize(28)
|
||||
.setType(ButtonControl.class)
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(CONTROL_WIDTH, ROW_HEIGHT)
|
||||
.withPositionRelative(10 + LABEL_WIDTH + SPACING, yPos);
|
||||
this.scrollArea.putRenderingBo(bo);
|
||||
|
||||
return yPos + ROW_HEIGHT + SPACING;
|
||||
}
|
||||
|
||||
private int addSliderRow(final String label, final String fieldId, final float value, final int yPos) {
|
||||
var bo = this.renderingBoPool.acquire("ThemeEditor_Label_" + fieldId, ControlRenderingBo.class)
|
||||
.setCaption(label)
|
||||
.setFontSize(30)
|
||||
.setType(LabelControl.class)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(LABEL_WIDTH, ROW_HEIGHT)
|
||||
.withPositionRelative(10, yPos + 10);
|
||||
this.scrollArea.putRenderingBo(bo);
|
||||
|
||||
bo = this.renderingBoPool.acquire(fieldId, ControlRenderingBo.class)
|
||||
.setType(SliderControl.class)
|
||||
.setControlData(new SliderData(true, value, value, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(CONTROL_WIDTH, ROW_HEIGHT)
|
||||
.withPositionRelative(10 + LABEL_WIDTH + SPACING, yPos);
|
||||
this.scrollArea.putRenderingBo(bo);
|
||||
|
||||
bo = this.renderingBoPool.acquire(fieldId + "_Value", ControlRenderingBo.class)
|
||||
.setCaption(this.formatValue(value))
|
||||
.setFontSize(26)
|
||||
.setType(LabelControl.class)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(VALUE_WIDTH, ROW_HEIGHT)
|
||||
.withPositionRelative(10 + LABEL_WIDTH + SPACING + CONTROL_WIDTH + SPACING, yPos + 10);
|
||||
this.scrollArea.putRenderingBo(bo);
|
||||
|
||||
return yPos + ROW_HEIGHT + SPACING;
|
||||
}
|
||||
|
||||
private int addColorRow(final String label, final String fieldId, final HSBColor color, final int yPos) {
|
||||
var bo = this.renderingBoPool.acquire("ThemeEditor_Label_" + fieldId, ControlRenderingBo.class)
|
||||
.setCaption(label)
|
||||
.setFontSize(30)
|
||||
.setType(LabelControl.class)
|
||||
.setControlData(new LabelData(null, null, HorizontalAlignment.LEFT))
|
||||
.setVisible(true)
|
||||
.setEnabled(false)
|
||||
.withDimensionAbsolute(LABEL_WIDTH, ROW_HEIGHT)
|
||||
.withPositionRelative(10, yPos + 10);
|
||||
this.scrollArea.putRenderingBo(bo);
|
||||
|
||||
final int rectWidth = CONTROL_WIDTH - VALUE_WIDTH - SPACING;
|
||||
final DrawEffectRenderingBo rectBo = this.renderingBoPool.acquire("ThemeEditor_ColorRect_" + fieldId, DrawEffectRenderingBo.class);
|
||||
rectBo.setEffect(DrawEffectRenderingBoEffects.RECT_FILLED);
|
||||
rectBo.setRelativeCoordinates(List.of(ImmutableCoordI2.create(rectWidth, ROW_HEIGHT)));
|
||||
rectBo.setColor(this.toRgbaColor(color));
|
||||
rectBo.withPositionRelative(10 + LABEL_WIDTH + SPACING, yPos);
|
||||
this.scrollArea.putRenderingBo(rectBo);
|
||||
|
||||
bo = this.renderingBoPool.acquire(fieldId, ControlRenderingBo.class)
|
||||
.setCaption("Edit")
|
||||
.setFontSize(26)
|
||||
.setType(ButtonControl.class)
|
||||
.setControlData(new ControlRenderingBo.ButtonData(null, null))
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.withDimensionAbsolute(VALUE_WIDTH, ROW_HEIGHT)
|
||||
.withPositionRelative(10 + LABEL_WIDTH + SPACING + rectWidth + SPACING, yPos);
|
||||
this.scrollArea.putRenderingBo(bo);
|
||||
|
||||
this.colorSlotByControlId.put(fieldId, this.resolveColorSlot(fieldId));
|
||||
|
||||
return yPos + ROW_HEIGHT + SPACING;
|
||||
}
|
||||
|
||||
private void onUiControlEvent(final UiControlEvent event) {
|
||||
if (event.getId() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.getId()) {
|
||||
case "ThemeEditor_Button_Save" -> this.onSave();
|
||||
case "ThemeEditor_Button_Cancel" -> this.onCancel();
|
||||
case "ThemeEditor_Field_Name" -> this.onNameChanged(event.getPayload());
|
||||
case "ThemeEditor_Field_Pattern" -> this.openPatternPopup();
|
||||
case "ThemeEditor_Field_Font" -> this.openFontPopup();
|
||||
case "ThemeEditor_Field_Hover" -> this.onHoverChanged(event.getPayload());
|
||||
case "ThemeEditor_Field_Pressed" -> this.onPressedChanged(event.getPayload());
|
||||
case "ThemeEditor_Popup_Menu" -> this.onPopupSelected(event.getPayload());
|
||||
default -> this.handleColorButton(event.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private void onNameChanged(final Object payload) {
|
||||
if (payload instanceof final String text) {
|
||||
this.name = text.trim();
|
||||
}
|
||||
}
|
||||
|
||||
private void onHoverChanged(final Object payload) {
|
||||
if (payload instanceof final Double value) {
|
||||
this.hoverBrightnessAdjustment = this.clamp01(value.floatValue());
|
||||
this.updateValueLabel("ThemeEditor_Field_Hover_Value", this.hoverBrightnessAdjustment);
|
||||
this.updateSliderValue("ThemeEditor_Field_Hover", value);
|
||||
this.editorWindow.draw();
|
||||
this.applyCurrentThemePreview();
|
||||
}
|
||||
}
|
||||
|
||||
private void onPressedChanged(final Object payload) {
|
||||
if (payload instanceof final Double value) {
|
||||
this.pressedBrightnessAdjustment = this.clamp01(value.floatValue());
|
||||
this.updateValueLabel("ThemeEditor_Field_Pressed_Value", this.pressedBrightnessAdjustment);
|
||||
this.updateSliderValue("ThemeEditor_Field_Pressed", value);
|
||||
this.editorWindow.draw();
|
||||
this.applyCurrentThemePreview();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleColorButton(final String controlId) {
|
||||
final var slot = this.colorSlotByControlId.get(controlId);
|
||||
if (slot == null) {
|
||||
return;
|
||||
}
|
||||
final var color = this.getColor(slot);
|
||||
final var requestId = "ThemeEditor_ColorPicker_" + slot.name();
|
||||
this.pendingColorRequestId = requestId;
|
||||
this.hsbColorPickerModule.open(requestId, slot.displayName, color, this.editorWindow);
|
||||
}
|
||||
|
||||
private void onColorPickerResponse(final HsbColorPickerResponseEvent event) {
|
||||
if (this.pendingColorRequestId == null || !this.pendingColorRequestId.equals(event.requestId())) {
|
||||
return;
|
||||
}
|
||||
if (!event.confirmed() || event.color() == null) {
|
||||
return;
|
||||
}
|
||||
final var slot = ColorSlot.valueOf(event.requestId().substring("ThemeEditor_ColorPicker_".length()));
|
||||
this.setColor(slot, event.color());
|
||||
this.updateColorButton(slot);
|
||||
this.applyCurrentThemePreview();
|
||||
this.editorWindow.draw();
|
||||
}
|
||||
|
||||
private void openPatternPopup() {
|
||||
final List<String> items = this.themeManager.getPatternRendererNames();
|
||||
if (this.popupTarget == null) {
|
||||
this.popupTarget = PopupTarget.PATTERN_RENDERER;
|
||||
this.showPopup(items, "ThemeEditor_Field_Pattern");
|
||||
}
|
||||
else {
|
||||
this.hidePopup();
|
||||
this.editorWindow.draw();
|
||||
}
|
||||
}
|
||||
|
||||
private void openFontPopup() {
|
||||
final List<String> items = this.getAvailableFonts();
|
||||
if (this.popupTarget == null) {
|
||||
this.popupTarget = PopupTarget.FONT;
|
||||
this.showPopup(items, "ThemeEditor_Field_Font");
|
||||
}
|
||||
else {
|
||||
this.hidePopup();
|
||||
this.editorWindow.draw();
|
||||
}
|
||||
}
|
||||
|
||||
private void onPopupSelected(final Object payload) {
|
||||
final String value;
|
||||
if (payload instanceof final String id) {
|
||||
value = id;
|
||||
}
|
||||
else if (payload instanceof final PopupMenuItem item) {
|
||||
value = item.id();
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
if (this.popupTarget == PopupTarget.PATTERN_RENDERER) {
|
||||
this.patternRendererName = value;
|
||||
this.updateButtonCaption("ThemeEditor_Field_Pattern", value);
|
||||
}
|
||||
else if (this.popupTarget == PopupTarget.FONT) {
|
||||
this.font = value;
|
||||
this.updateButtonCaption("ThemeEditor_Field_Font", value);
|
||||
}
|
||||
|
||||
this.hidePopup();
|
||||
this.editorWindow.draw();
|
||||
this.applyCurrentThemePreview();
|
||||
}
|
||||
|
||||
private void showPopup(final List<String> items, final String anchorId) {
|
||||
if (items == null || items.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
final var bo = this.editorWindow.getRenderingBo(anchorId, ControlRenderingBo.class);
|
||||
if (!(bo instanceof final RenderingBoHasDimension boDim)) {
|
||||
return;
|
||||
}
|
||||
final var popupItems = items.stream()
|
||||
.map(item -> new PopupMenuItem(item, item, true))
|
||||
.toList();
|
||||
final var rowHeight = 32;
|
||||
final var padding = 8;
|
||||
final var width = Math.min(420, boDim.getWidth());
|
||||
final var height = (rowHeight * popupItems.size()) + (padding * 2);
|
||||
|
||||
final var offset = this.scrollArea.getRelativePosition();
|
||||
final var x = offset.getX() + bo.getLocalX();
|
||||
final var y = offset.getY() + bo.getLocalY() + boDim.getHeight() + 6;
|
||||
|
||||
final var data = new PopupMenuData(popupItems, null, null, null, rowHeight, padding);
|
||||
final var popup = this.renderingBoPool.acquire("ThemeEditor_Popup_Menu", ControlRenderingBo.class)
|
||||
.setType(PopupMenuControl.class)
|
||||
.setFontSize(22)
|
||||
.setVisible(true)
|
||||
.setEnabled(true)
|
||||
.setControlData(data)
|
||||
.withDimensionAbsolute(width, height)
|
||||
.setLayer(RenderingBoLayer.UI1)
|
||||
.withPositionRelative(x, y);
|
||||
this.editorWindow.putRenderingBo(popup);
|
||||
this.editorWindow.draw();
|
||||
}
|
||||
|
||||
private void hidePopup() {
|
||||
this.editorWindow.removeRenderingBo("ThemeEditor_Popup_Menu");
|
||||
this.popupTarget = null;
|
||||
}
|
||||
|
||||
private void updateButtonCaption(final String id, final String caption) {
|
||||
final var bo = this.editorWindow.getRenderingBo(id, ControlRenderingBo.class);
|
||||
if (bo != null) {
|
||||
bo.setCaption(caption != null ? caption : "");
|
||||
this.editorWindow.putRenderingBo(bo);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateValueLabel(final String id, final float value) {
|
||||
final var bo = this.editorWindow.getRenderingBo(id, ControlRenderingBo.class);
|
||||
if (bo != null) {
|
||||
bo.setCaption(this.formatValue(value));
|
||||
this.editorWindow.putRenderingBo(bo);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSliderValue(final String id, final double value) {
|
||||
final var bo = this.editorWindow.getRenderingBo(id, ControlRenderingBo.class);
|
||||
if (bo != null) {
|
||||
final var sliderData = new SliderData(true, value, value, null);
|
||||
bo.setControlData(sliderData);
|
||||
this.editorWindow.putRenderingBo(bo);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateColorButton(final ColorSlot slot) {
|
||||
final var controlId = slot.controlId;
|
||||
final var rectBo = this.editorWindow.getRenderingBo("ThemeEditor_ColorRect_" + controlId, DrawEffectRenderingBo.class);
|
||||
if (rectBo != null) {
|
||||
rectBo.setColor(this.toRgbaColor(this.getColor(slot)));
|
||||
this.editorWindow.putRenderingBo(rectBo);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getAvailableFonts() {
|
||||
final var fonts = new ArrayList<String>();
|
||||
for (final var name : this.staticResourceService.listPath(List.of("renderer"))) {
|
||||
final var lower = name.toLowerCase();
|
||||
if (lower.endsWith(".ttf") || lower.endsWith(".otf")) {
|
||||
fonts.add(name);
|
||||
}
|
||||
}
|
||||
fonts.sort(String::compareToIgnoreCase);
|
||||
return fonts;
|
||||
}
|
||||
|
||||
private void onSave() {
|
||||
if (this.name == null || this.name.isBlank()) {
|
||||
return;
|
||||
}
|
||||
final var theme = new Theme(
|
||||
this.name.trim(),
|
||||
this.patternRendererName,
|
||||
this.font,
|
||||
this.borderOuter,
|
||||
this.borderInner,
|
||||
this.borderInnerDisabled,
|
||||
this.backgroundNormal,
|
||||
this.backgroundSelected,
|
||||
this.textNormal,
|
||||
this.textDisabled,
|
||||
this.selectionPrimary,
|
||||
this.selectionSecondary,
|
||||
this.windowBackground,
|
||||
this.hoverBrightnessAdjustment,
|
||||
this.pressedBrightnessAdjustment);
|
||||
this.storageService.store(CUSTOM_THEME_PATH, theme.getName(), theme);
|
||||
if (this.originalName != null && !this.originalName.equalsIgnoreCase(theme.getName())) {
|
||||
this.storageService.deleteByPath(CUSTOM_THEME_PATH, this.originalName);
|
||||
}
|
||||
this.themeManager.setCurrentTheme(theme);
|
||||
this.eventService.fire(new OptionsThemeEditorSavedEvent(true, theme));
|
||||
this.close();
|
||||
}
|
||||
|
||||
private void onCancel() {
|
||||
if (this.originalTheme != null) {
|
||||
this.themeManager.setCurrentTheme(this.originalTheme);
|
||||
}
|
||||
this.eventService.fire(new OptionsThemeEditorSavedEvent(false, null));
|
||||
this.close();
|
||||
}
|
||||
|
||||
private void applyCurrentThemePreview() {
|
||||
final var previewTheme = new Theme(
|
||||
this.name != null && !this.name.isBlank() ? this.name : "Preview",
|
||||
this.patternRendererName,
|
||||
this.font,
|
||||
this.borderOuter,
|
||||
this.borderInner,
|
||||
this.borderInnerDisabled,
|
||||
this.backgroundNormal,
|
||||
this.backgroundSelected,
|
||||
this.textNormal,
|
||||
this.textDisabled,
|
||||
this.selectionPrimary,
|
||||
this.selectionSecondary,
|
||||
this.windowBackground,
|
||||
this.hoverBrightnessAdjustment,
|
||||
this.pressedBrightnessAdjustment);
|
||||
this.themeManager.setCurrentTheme(previewTheme);
|
||||
}
|
||||
|
||||
private void close() {
|
||||
this.getModuleService().changeActiveState(this.getId(), false);
|
||||
this.getModuleService().changeDisplayState(this.getId(), false);
|
||||
}
|
||||
|
||||
private float clamp01(final float value) {
|
||||
return Math.max(0.0f, Math.min(1.0f, value));
|
||||
}
|
||||
|
||||
private String formatValue(final float value) {
|
||||
return String.format("%.2f", value);
|
||||
}
|
||||
|
||||
private HSBColor getColor(final ColorSlot slot) {
|
||||
return switch (slot) {
|
||||
case BORDER_OUTER -> this.borderOuter;
|
||||
case BORDER_INNER -> this.borderInner;
|
||||
case BORDER_INNER_DISABLED -> this.borderInnerDisabled;
|
||||
case BACKGROUND_NORMAL -> this.backgroundNormal;
|
||||
case BACKGROUND_SELECTED -> this.backgroundSelected;
|
||||
case TEXT_NORMAL -> this.textNormal;
|
||||
case TEXT_DISABLED -> this.textDisabled;
|
||||
case SELECTION_PRIMARY -> this.selectionPrimary;
|
||||
case SELECTION_SECONDARY -> this.selectionSecondary;
|
||||
case WINDOW_BACKGROUND -> this.windowBackground;
|
||||
};
|
||||
}
|
||||
|
||||
private void setColor(final ColorSlot slot, final HSBColor color) {
|
||||
switch (slot) {
|
||||
case BORDER_OUTER -> this.borderOuter = color;
|
||||
case BORDER_INNER -> this.borderInner = color;
|
||||
case BORDER_INNER_DISABLED -> this.borderInnerDisabled = color;
|
||||
case BACKGROUND_NORMAL -> this.backgroundNormal = color;
|
||||
case BACKGROUND_SELECTED -> this.backgroundSelected = color;
|
||||
case TEXT_NORMAL -> this.textNormal = color;
|
||||
case TEXT_DISABLED -> this.textDisabled = color;
|
||||
case SELECTION_PRIMARY -> this.selectionPrimary = color;
|
||||
case SELECTION_SECONDARY -> this.selectionSecondary = color;
|
||||
case WINDOW_BACKGROUND -> this.windowBackground = color;
|
||||
}
|
||||
}
|
||||
|
||||
private ImmutableRgbaColor toRgbaColor(final HSBColor color) {
|
||||
return new ImmutableRgbaColor(color.toColor());
|
||||
}
|
||||
|
||||
private ColorSlot resolveColorSlot(final String controlId) {
|
||||
return switch (controlId) {
|
||||
case "ThemeEditor_Color_BorderOuter" -> ColorSlot.BORDER_OUTER;
|
||||
case "ThemeEditor_Color_BorderInner" -> ColorSlot.BORDER_INNER;
|
||||
case "ThemeEditor_Color_BorderInnerDisabled" -> ColorSlot.BORDER_INNER_DISABLED;
|
||||
case "ThemeEditor_Color_BackgroundNormal" -> ColorSlot.BACKGROUND_NORMAL;
|
||||
case "ThemeEditor_Color_BackgroundSelected" -> ColorSlot.BACKGROUND_SELECTED;
|
||||
case "ThemeEditor_Color_TextNormal" -> ColorSlot.TEXT_NORMAL;
|
||||
case "ThemeEditor_Color_TextDisabled" -> ColorSlot.TEXT_DISABLED;
|
||||
case "ThemeEditor_Color_SelectionPrimary" -> ColorSlot.SELECTION_PRIMARY;
|
||||
case "ThemeEditor_Color_SelectionSecondary" -> ColorSlot.SELECTION_SECONDARY;
|
||||
case "ThemeEditor_Color_WindowBackground" -> ColorSlot.WINDOW_BACKGROUND;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private ImmutableCoordI2 centeredPosition(final de.extio.game_engine.spatial2.model.CoordI2 dimension) {
|
||||
final var referenceResolution = RendererControl.REFERENCE_RESOLUTION;
|
||||
final var x = Math.max(0, (referenceResolution.getX() - dimension.getX()) / 2);
|
||||
final var y = Math.max(0, (referenceResolution.getY() - dimension.getY()) / 2);
|
||||
return ImmutableCoordI2.create(x, y);
|
||||
}
|
||||
|
||||
private enum PopupTarget {
|
||||
PATTERN_RENDERER,
|
||||
FONT
|
||||
}
|
||||
|
||||
private enum ColorSlot {
|
||||
|
||||
BORDER_OUTER("ThemeEditor_Color_BorderOuter", "Border Outer"),
|
||||
BORDER_INNER("ThemeEditor_Color_BorderInner", "Border Inner"),
|
||||
BORDER_INNER_DISABLED("ThemeEditor_Color_BorderInnerDisabled", "Border Inner Disabled"),
|
||||
BACKGROUND_NORMAL("ThemeEditor_Color_BackgroundNormal", "Background Normal"),
|
||||
BACKGROUND_SELECTED("ThemeEditor_Color_BackgroundSelected", "Background Selected"),
|
||||
TEXT_NORMAL("ThemeEditor_Color_TextNormal", "Text Normal"),
|
||||
TEXT_DISABLED("ThemeEditor_Color_TextDisabled", "Text Disabled"),
|
||||
SELECTION_PRIMARY("ThemeEditor_Color_SelectionPrimary", "Selection Primary"),
|
||||
SELECTION_SECONDARY("ThemeEditor_Color_SelectionSecondary", "Selection Secondary"),
|
||||
WINDOW_BACKGROUND("ThemeEditor_Color_WindowBackground", "Window Background");
|
||||
|
||||
private final String controlId;
|
||||
|
||||
private final String displayName;
|
||||
|
||||
ColorSlot(final String controlId, final String displayName) {
|
||||
this.controlId = controlId;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,3 +7,4 @@ de.extio.game_engine.module.ModuleServiceAutoConfiguration
|
||||
de.extio.game_engine.i18n.LocalizationAutoConfiguration
|
||||
de.extio.game_engine.resource.StaticResourceAutoConfiguration
|
||||
de.extio.game_engine.audio.AudioAutoConfiguration
|
||||
de.extio.game_engine.menu.MenuAutoConfiguration
|
||||
|
||||
Reference in New Issue
Block a user