mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
Add enum form element and update screenshot dialog
The enum form element allows for cycling through a list of options. The screenshot dialog now uses this, and has a list of image qualities. The map quality has not yet been implemented.
This commit is contained in:
parent
da6b1a437c
commit
96170f8d22
@ -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 {
|
> .buttons {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { BaseItem } from "../game/base_item";
|
import { BaseItem } from "../game/base_item";
|
||||||
import { ClickDetector } from "./click_detector";
|
import { ClickDetector } from "./click_detector";
|
||||||
import { Signal } from "./signal";
|
import { Signal } from "./signal";
|
||||||
|
import { safeModulo } from "./utils";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ***************************************************
|
* ***************************************************
|
||||||
@ -235,3 +236,56 @@ export class FormElementItemChooser extends FormElement {
|
|||||||
|
|
||||||
focus() {}
|
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 `
|
||||||
|
<div class="formElement enumFormElem">
|
||||||
|
${this.label ? `<label>${this.label}</label>` : ""}
|
||||||
|
<div class="enum" data-formId="${this.id}">
|
||||||
|
<div class="toggle prev">⯇</div>
|
||||||
|
<div class="value">${this.textGetter(this.options[0])}</div>
|
||||||
|
<div class="toggle next">⯈</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {HTMLElement} parent
|
||||||
|
* @param {Array<ClickDetector>} 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() {}
|
||||||
|
}
|
||||||
|
@ -9,10 +9,38 @@ import { StaticMapEntityComponent } from "../../components/static_map_entity";
|
|||||||
import { KEYMAPPINGS } from "../../key_action_mapper";
|
import { KEYMAPPINGS } from "../../key_action_mapper";
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { DialogWithForm } from "../../../core/modal_dialog_elements";
|
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");
|
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 {
|
export class HUDScreenshotExporter extends BaseHUDPart {
|
||||||
createElements() {}
|
createElements() {}
|
||||||
|
|
||||||
@ -26,23 +54,24 @@ export class HUDScreenshotExporter extends BaseHUDPart {
|
|||||||
return;
|
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({
|
const layerInput = new FormElementCheckbox({
|
||||||
id: "screenshotLayer",
|
id: "screenshotLayer",
|
||||||
|
// @TODO: translation (T.dialogs.exportScreenshotWarning.descLayer)
|
||||||
label: "Include wires layer",
|
label: "Include wires layer",
|
||||||
defaultValue: this.root.currentLayer === "wires" ? true : false,
|
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({
|
const dialog = new DialogWithForm({
|
||||||
app: this.root.app,
|
app: this.root.app,
|
||||||
title: T.dialogs.exportScreenshotWarning.title,
|
title: T.dialogs.exportScreenshotWarning.title,
|
||||||
desc: T.dialogs.exportScreenshotWarning.desc,
|
desc: T.dialogs.exportScreenshotWarning.desc,
|
||||||
formElements: [layerInput, qualityInput],
|
formElements: [qualityInput, layerInput],
|
||||||
buttons: ["cancel:good", "ok:bad"],
|
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 ...");
|
logger.log("Starting export ...");
|
||||||
|
|
||||||
// Find extends
|
// Find extends
|
||||||
@ -76,12 +109,15 @@ export class HUDScreenshotExporter extends BaseHUDPart {
|
|||||||
const dimensions = maxChunk.sub(minChunk);
|
const dimensions = maxChunk.sub(minChunk);
|
||||||
logger.log("Dimensions:", dimensions);
|
logger.log("Dimensions:", dimensions);
|
||||||
|
|
||||||
let chunkSizePixels = quality * globalConfig.mapChunkSize;
|
|
||||||
const maxDimensions = Math.max(dimensions.x, dimensions.y);
|
const maxDimensions = Math.max(dimensions.x, dimensions.y);
|
||||||
|
|
||||||
if (maxDimensions > 128) {
|
// we want integer pixels per tile
|
||||||
chunkSizePixels = Math.max(1, Math.floor(chunkSizePixels * (128 / maxDimensions)));
|
// 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);
|
logger.log("ChunkSizePixels:", chunkSizePixels);
|
||||||
|
|
||||||
const chunkScale = chunkSizePixels / globalConfig.mapChunkWorldSize;
|
const chunkScale = chunkSizePixels / globalConfig.mapChunkWorldSize;
|
||||||
|
Loading…
Reference in New Issue
Block a user