diff --git a/src/css/ingame_hud/dialogs.scss b/src/css/ingame_hud/dialogs.scss
index cc742d42..38e359e4 100644
--- a/src/css/ingame_hud/dialogs.scss
+++ b/src/css/ingame_hud/dialogs.scss
@@ -214,6 +214,53 @@
}
}
}
+
+ .checkBoxFormElem {
+ @include S(margin, 10px, 0);
+ > label {
+ @include S(margin-right, 10px);
+ }
+ display: flex;
+ align-items: center;
+ }
+
+ .enum {
+ @include S(margin, 10px, 0);
+ display: grid;
+ grid-template-columns: auto 1fr auto;
+ @include S(grid-gap, 4px);
+ @include S(width, 200px);
+
+ > * {
+ background: #fff;
+ text-align: center;
+ pointer-events: all;
+ cursor: pointer;
+ @include S(border-radius, $globalBorderRadius);
+ @include S(padding, 4px);
+
+ transition: background-color 0.12s ease-in-out;
+ &:hover {
+ background-color: #fafafa;
+ }
+
+ @include DarkThemeOverride {
+ background-color: $darkModeControlsBackground;
+ color: #ddd;
+ &:hover {
+ background-color: darken($darkModeControlsBackground, 2);
+ }
+ }
+
+ &.toggle {
+ @include S(width, 20px);
+ }
+
+ &.value {
+ transform: none !important;
+ }
+ }
+ }
}
> .buttons {
diff --git a/src/js/core/modal_dialog_forms.js b/src/js/core/modal_dialog_forms.js
index aac81d82..9242c57e 100644
--- a/src/js/core/modal_dialog_forms.js
+++ b/src/js/core/modal_dialog_forms.js
@@ -1,6 +1,7 @@
import { BaseItem } from "../game/base_item";
import { ClickDetector } from "./click_detector";
import { Signal } from "./signal";
+import { safeModulo } from "./utils";
/*
* ***************************************************
@@ -235,3 +236,56 @@ export class FormElementItemChooser extends FormElement {
focus() {}
}
+
+export class FormElementEnum extends FormElement {
+ constructor({ id, label = null, options, valueGetter, textGetter }) {
+ super(id, label);
+ this.options = options;
+ this.valueGetter = valueGetter;
+ this.textGetter = textGetter;
+ this.index = 0;
+
+ this.element = null;
+ }
+
+ getHtml() {
+ return `
+
+ `;
+ }
+
+ /**
+ * @param {HTMLElement} parent
+ * @param {Array} clickTrackers
+ */
+ bindEvents(parent, clickTrackers) {
+ this.element = this.getFormElement(parent);
+
+ const children = this.element.children;
+ for (let i = 0; i < children.length; ++i) {
+ const child = children[i];
+ const detector = new ClickDetector(child, { preventDefault: false });
+ clickTrackers.push(detector);
+ const change = child.classList.contains("prev") ? -1 : 1;
+ detector.click.add(() => this.toggle(change), this);
+ }
+ }
+
+ getValue() {
+ return this.valueGetter(this.options[this.index]);
+ }
+
+ toggle(amount) {
+ this.index = safeModulo(this.index + amount, this.options.length);
+ this.element.querySelector(".value").innerText = this.textGetter(this.options[this.index]);
+ }
+
+ focus() {}
+}
diff --git a/src/js/game/hud/parts/screenshot_exporter.js b/src/js/game/hud/parts/screenshot_exporter.js
index 31e90a4f..0057e03c 100644
--- a/src/js/game/hud/parts/screenshot_exporter.js
+++ b/src/js/game/hud/parts/screenshot_exporter.js
@@ -9,10 +9,38 @@ import { StaticMapEntityComponent } from "../../components/static_map_entity";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { BaseHUDPart } from "../base_hud_part";
import { DialogWithForm } from "../../../core/modal_dialog_elements";
-import { FormElementInput, FormElementCheckbox } from "../../../core/modal_dialog_forms";
+import { FormElementInput, FormElementCheckbox, FormElementEnum } from "../../../core/modal_dialog_forms";
const logger = createLogger("screenshot_exporter");
+/**
+ * @typedef {{mode: string, resolution?: number}} QualityOptions
+ */
+
+/**
+ * @type {{id: string, options: QualityOptions}[]}
+ */
+const screenshotQualities = [
+ {
+ id: "high",
+ options: { mode: "regular", resolution: 16384 },
+ },
+ {
+ id: "medium",
+ options: { mode: "regular", resolution: 4096 },
+ },
+ {
+ id: "low",
+ options: { mode: "regular", resolution: 1024 },
+ },
+ {
+ id: "map",
+ options: { mode: "map" },
+ },
+];
+// @TODO: translation (T.dialogs.exportScreenshotWarning.qualities)
+const qualityNames = { high: "High", medium: "Medium", low: "Low", map: "Map" };
+
export class HUDScreenshotExporter extends BaseHUDPart {
createElements() {}
@@ -26,23 +54,24 @@ export class HUDScreenshotExporter extends BaseHUDPart {
return;
}
+ const qualityInput = new FormElementEnum({
+ id: "screenshotQuality",
+ options: screenshotQualities,
+ valueGetter: quality => quality.options,
+ // @TODO: translation (T.dialogs.exportScreenshotWarning.qualityLabel)
+ textGetter: quality => "Quality:" + " " + qualityNames[quality.id],
+ });
const layerInput = new FormElementCheckbox({
id: "screenshotLayer",
+ // @TODO: translation (T.dialogs.exportScreenshotWarning.descLayer)
label: "Include wires layer",
defaultValue: this.root.currentLayer === "wires" ? true : false,
});
- const qualityInput = new FormElementInput({
- id: "screenshotQuality",
- label: "Pixel width per tile",
- placeholder: "",
- defaultValue: "",
- validator: val => !isNaN(val) && parseInt(val) === parseFloat(val) && !isNaN(parseInt(val, 10)),
- });
const dialog = new DialogWithForm({
app: this.root.app,
title: T.dialogs.exportScreenshotWarning.title,
desc: T.dialogs.exportScreenshotWarning.desc,
- formElements: [layerInput, qualityInput],
+ formElements: [qualityInput, layerInput],
buttons: ["cancel:good", "ok:bad"],
});
@@ -53,7 +82,11 @@ export class HUDScreenshotExporter extends BaseHUDPart {
);
}
- doExport(wiresLayer, quality) {
+ /**
+ * @param {boolean} wiresLayer
+ * @param {QualityOptions} options
+ */
+ doExport(wiresLayer, options) {
logger.log("Starting export ...");
// Find extends
@@ -76,12 +109,15 @@ export class HUDScreenshotExporter extends BaseHUDPart {
const dimensions = maxChunk.sub(minChunk);
logger.log("Dimensions:", dimensions);
- let chunkSizePixels = quality * globalConfig.mapChunkSize;
const maxDimensions = Math.max(dimensions.x, dimensions.y);
- if (maxDimensions > 128) {
- chunkSizePixels = Math.max(1, Math.floor(chunkSizePixels * (128 / maxDimensions)));
- }
+ // we want integer pixels per tile
+ // if resolution too low, we want integer pixels per chunk
+ const chunkSizePixels =
+ maxDimensions * globalConfig.mapChunkSize > options.resolution
+ ? Math.max(1, Math.floor(options.resolution / maxDimensions))
+ : Math.floor(options.resolution / (maxDimensions * globalConfig.mapChunkSize)) *
+ globalConfig.mapChunkSize;
logger.log("ChunkSizePixels:", chunkSizePixels);
const chunkScale = chunkSizePixels / globalConfig.mapChunkWorldSize;