DPI
This commit is contained in:
parent
9f206eb39d
commit
6aa3b74348
8 changed files with 120 additions and 8 deletions
|
|
@ -86,3 +86,5 @@ tlEditorCmd='auto'
|
||||||
# Hotkeys (event.key names, e.g. F9, F10, Control)
|
# Hotkeys (event.key names, e.g. F9, F10, Control)
|
||||||
tlHotkey='F9'
|
tlHotkey='F9'
|
||||||
forgeHotkey='F10'
|
forgeHotkey='F10'
|
||||||
|
# UI scale for in-game overlays: auto (match game width) or a number like 1.5 / 2
|
||||||
|
playtestUiScale='auto'
|
||||||
|
|
|
||||||
|
|
@ -2551,6 +2551,32 @@ class WorkflowTab(QWidget):
|
||||||
hotkey_row.addStretch(1)
|
hotkey_row.addStretch(1)
|
||||||
settings_inner.addLayout(hotkey_row)
|
settings_inner.addLayout(hotkey_row)
|
||||||
|
|
||||||
|
scale_row = QHBoxLayout()
|
||||||
|
scale_row.setSpacing(8)
|
||||||
|
scale_lbl = QLabel("UI scale:")
|
||||||
|
scale_lbl.setFixedWidth(_PT_LABEL_W)
|
||||||
|
scale_lbl.setStyleSheet(_PT_LBL_STYLE)
|
||||||
|
scale_lbl.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||||||
|
scale_row.addWidget(scale_lbl)
|
||||||
|
|
||||||
|
self._pt_ui_scale_combo = QComboBox()
|
||||||
|
self._pt_ui_scale_combo.setToolTip(
|
||||||
|
"In-game overlay size. Auto scales from game resolution and display DPI."
|
||||||
|
)
|
||||||
|
for label, value in (
|
||||||
|
("Auto (match game width)", "auto"),
|
||||||
|
("100%", "1"),
|
||||||
|
("125%", "1.25"),
|
||||||
|
("150%", "1.5"),
|
||||||
|
("175%", "1.75"),
|
||||||
|
("200%", "2"),
|
||||||
|
("225%", "2.25"),
|
||||||
|
("250%", "2.5"),
|
||||||
|
):
|
||||||
|
self._pt_ui_scale_combo.addItem(label, value)
|
||||||
|
scale_row.addWidget(self._pt_ui_scale_combo, 1)
|
||||||
|
settings_inner.addLayout(scale_row)
|
||||||
|
|
||||||
editor_title = QLabel("Editor settings")
|
editor_title = QLabel("Editor settings")
|
||||||
editor_title.setStyleSheet(_PT_SECTION_STYLE + "padding-top:2px;")
|
editor_title.setStyleSheet(_PT_SECTION_STYLE + "padding-top:2px;")
|
||||||
settings_inner.addWidget(editor_title)
|
settings_inner.addWidget(editor_title)
|
||||||
|
|
@ -2753,11 +2779,19 @@ class WorkflowTab(QWidget):
|
||||||
cfg = {
|
cfg = {
|
||||||
"hotkey": "F9",
|
"hotkey": "F9",
|
||||||
"forgeHotkey": "F10",
|
"forgeHotkey": "F10",
|
||||||
|
"uiScale": "auto",
|
||||||
"editorCmd": "auto",
|
"editorCmd": "auto",
|
||||||
}
|
}
|
||||||
|
|
||||||
self._pt_hotkey_edit.setText(cfg.get("hotkey", "F9"))
|
self._pt_hotkey_edit.setText(cfg.get("hotkey", "F9"))
|
||||||
self._pt_forge_hotkey_edit.setText(cfg.get("forgeHotkey", "F10"))
|
self._pt_forge_hotkey_edit.setText(cfg.get("forgeHotkey", "F10"))
|
||||||
|
want_scale = str(cfg.get("uiScale", "auto"))
|
||||||
|
scale_idx = self._pt_ui_scale_combo.findData(want_scale)
|
||||||
|
if scale_idx >= 0:
|
||||||
|
self._pt_ui_scale_combo.setCurrentIndex(scale_idx)
|
||||||
|
else:
|
||||||
|
custom_idx = self._pt_ui_scale_combo.findData("auto")
|
||||||
|
self._pt_ui_scale_combo.setCurrentIndex(custom_idx if custom_idx >= 0 else 0)
|
||||||
self._populate_tli_editor_combo(select=cfg.get("editorCmd", "auto"))
|
self._populate_tli_editor_combo(select=cfg.get("editorCmd", "auto"))
|
||||||
|
|
||||||
def _resolve_playtest_config(self) -> dict:
|
def _resolve_playtest_config(self) -> dict:
|
||||||
|
|
@ -2773,6 +2807,7 @@ class WorkflowTab(QWidget):
|
||||||
return {
|
return {
|
||||||
"hotkey": self._pt_hotkey_edit.text().strip() or "F9",
|
"hotkey": self._pt_hotkey_edit.text().strip() or "F9",
|
||||||
"forgeHotkey": self._pt_forge_hotkey_edit.text().strip() or "F10",
|
"forgeHotkey": self._pt_forge_hotkey_edit.text().strip() or "F10",
|
||||||
|
"uiScale": str(self._pt_ui_scale_combo.currentData() or "auto"),
|
||||||
"editorCmd": editor,
|
"editorCmd": editor,
|
||||||
"workspaceFolder": "auto",
|
"workspaceFolder": "auto",
|
||||||
}
|
}
|
||||||
|
|
@ -2785,7 +2820,7 @@ class WorkflowTab(QWidget):
|
||||||
self._log(
|
self._log(
|
||||||
"✅ Playtest settings saved — "
|
"✅ Playtest settings saved — "
|
||||||
f"inspector={cfg['hotkey']}, forge={cfg['forgeHotkey']}, "
|
f"inspector={cfg['hotkey']}, forge={cfg['forgeHotkey']}, "
|
||||||
f"editor={cfg['editorCmd']}"
|
f"scale={cfg['uiScale']}, editor={cfg['editorCmd']}"
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self._log(f"❌ Could not save playtest settings: {exc}")
|
self._log(f"❌ Could not save playtest settings: {exc}")
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,12 @@
|
||||||
* @min 0
|
* @min 0
|
||||||
* @default 0
|
* @default 0
|
||||||
*
|
*
|
||||||
|
* @param uiScale
|
||||||
|
* @text UI Scale
|
||||||
|
* @desc Overlay size. Use auto to match game width, or a number like 1.5 or 2.
|
||||||
|
* @type string
|
||||||
|
* @default auto
|
||||||
|
*
|
||||||
* @command open
|
* @command open
|
||||||
* @text Open / Toggle Panel
|
* @text Open / Toggle Panel
|
||||||
* @desc Toggle the Forge overlay.
|
* @desc Toggle the Forge overlay.
|
||||||
|
|
@ -416,6 +422,18 @@
|
||||||
var LS = 'forge:';
|
var LS = 'forge:';
|
||||||
function store(k, v) { try { localStorage.setItem(LS + k, JSON.stringify(v)); } catch (e) {} }
|
function store(k, v) { try { localStorage.setItem(LS + k, JSON.stringify(v)); } catch (e) {} }
|
||||||
function load(k, d) { try { var s = localStorage.getItem(LS + k); return s == null ? d : JSON.parse(s); } catch (e) { return d; } }
|
function load(k, d) { try { var s = localStorage.getItem(LS + k); return s == null ? d : JSON.parse(s); } catch (e) { return d; } }
|
||||||
|
function resolveUiScale(v) {
|
||||||
|
if (v !== 'auto' && v != null && String(v).trim() !== '') {
|
||||||
|
var n = parseFloat(v);
|
||||||
|
if (!isNaN(n) && n > 0) return Math.max(0.75, Math.min(3, n));
|
||||||
|
}
|
||||||
|
var base = 816;
|
||||||
|
var w = (typeof Graphics !== 'undefined' && Graphics.width) ? Graphics.width : (window.innerWidth || base);
|
||||||
|
var scale = w / base;
|
||||||
|
var dpr = window.devicePixelRatio || 1;
|
||||||
|
if (dpr > 1.15) scale *= Math.min(2, 0.75 + dpr * 0.35);
|
||||||
|
return Math.max(1, Math.min(2.75, scale));
|
||||||
|
}
|
||||||
// persisted speed settings (UI-controlled; override core/param defaults if the user changed them)
|
// persisted speed settings (UI-controlled; override core/param defaults if the user changed them)
|
||||||
API._speedKey = load('speedKey', API._speedKey || 'Control');
|
API._speedKey = load('speedKey', API._speedKey || 'Control');
|
||||||
API._speedUseKey = load('speedUseKey', true);
|
API._speedUseKey = load('speedUseKey', true);
|
||||||
|
|
@ -524,6 +542,8 @@ input::placeholder{color:var(--fg-text-faint)}\
|
||||||
var shadow = host.attachShadow({ mode: 'open' });
|
var shadow = host.attachShadow({ mode: 'open' });
|
||||||
var rootEl = el('div', { id: 'forge-root' });
|
var rootEl = el('div', { id: 'forge-root' });
|
||||||
rootEl.setAttribute('data-theme', load('theme', 'dark'));
|
rootEl.setAttribute('data-theme', load('theme', 'dark'));
|
||||||
|
var uiScale = resolveUiScale(API._uiScale || 'auto');
|
||||||
|
if (uiScale !== 1) rootEl.style.zoom = String(uiScale);
|
||||||
shadow.appendChild(el('style', { text: CSS }));
|
shadow.appendChild(el('style', { text: CSS }));
|
||||||
shadow.appendChild(rootEl);
|
shadow.appendChild(rootEl);
|
||||||
|
|
||||||
|
|
@ -1080,6 +1100,7 @@ input::placeholder{color:var(--fg-text-faint)}\
|
||||||
if (window.Forge) {
|
if (window.Forge) {
|
||||||
window.Forge._hotkey = (P.hotkey || 'F10').trim();
|
window.Forge._hotkey = (P.hotkey || 'F10').trim();
|
||||||
window.Forge._itemMaxOverride = Number(P.itemMaxOverride) || 0;
|
window.Forge._itemMaxOverride = Number(P.itemMaxOverride) || 0;
|
||||||
|
window.Forge._uiScale = (P.uiScale || 'auto').trim();
|
||||||
try { if (!localStorage.getItem('forge:speedKey')) window.Forge._speedKey = (P.speedKey || 'Control').trim(); } catch (e) { window.Forge._speedKey = (P.speedKey || 'Control').trim(); }
|
try { if (!localStorage.getItem('forge:speedKey')) window.Forge._speedKey = (P.speedKey || 'Control').trim(); } catch (e) { window.Forge._speedKey = (P.speedKey || 'Control').trim(); }
|
||||||
}
|
}
|
||||||
if (typeof PluginManager !== 'undefined' && PluginManager.registerCommand) {
|
if (typeof PluginManager !== 'undefined' && PluginManager.registerCommand) {
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,14 @@ def _js_literal(value) -> str:
|
||||||
return json.dumps(str(value))
|
return json.dumps(str(value))
|
||||||
|
|
||||||
|
|
||||||
def plugin_entry(hotkey: str) -> str:
|
def plugin_entry(hotkey: str, ui_scale: str = "auto") -> str:
|
||||||
hk = _js_literal(hotkey.strip() or "F10")
|
hk = _js_literal(hotkey.strip() or "F10")
|
||||||
|
scale = _js_literal(ui_scale.strip() or "auto")
|
||||||
return (
|
return (
|
||||||
f' {{ "name": "Forge_MZ", "status": true, '
|
f' {{ "name": "Forge_MZ", "status": true, '
|
||||||
f'"description": "Forge — in-game cheat & editor overlay", '
|
f'"description": "Forge — in-game cheat & editor overlay", '
|
||||||
f'"parameters": {{ "hotkey": {hk}, "speedKey": "Control", '
|
f'"parameters": {{ "hotkey": {hk}, "speedKey": "Control", '
|
||||||
f'"startOpen": "false", "itemMaxOverride": "0" }} }}'
|
f'"startOpen": "false", "itemMaxOverride": "0", "uiScale": {scale} }} }}'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -48,7 +49,18 @@ def _patch_forge_hotkey(forge_text: str, hotkey: str) -> str:
|
||||||
|
|
||||||
|
|
||||||
def prepare_forge_mz_js(source: Path | None = None, cfg: dict | None = None) -> str:
|
def prepare_forge_mz_js(source: Path | None = None, cfg: dict | None = None) -> str:
|
||||||
"""Build Forge_MZ.js with the configured toggle hotkey."""
|
"""Build Forge_MZ.js with the configured toggle hotkey and UI scale."""
|
||||||
src = source or BUNDLED_PLUGIN
|
src = source or BUNDLED_PLUGIN
|
||||||
effective = {**load_config(), **(cfg or {})}
|
effective = {**load_config(), **(cfg or {})}
|
||||||
return _patch_forge_hotkey(src.read_text(encoding="utf-8"), effective["forgeHotkey"])
|
text = _patch_forge_hotkey(src.read_text(encoding="utf-8"), effective["forgeHotkey"])
|
||||||
|
scale = effective.get("uiScale", "auto")
|
||||||
|
text, n = re.subn(
|
||||||
|
r"(\* @param uiScale\s*\n(?:\s*\*[^\n]*\n)*?\s*\* @default )auto",
|
||||||
|
rf"\g<1>{scale}",
|
||||||
|
text,
|
||||||
|
count=1,
|
||||||
|
)
|
||||||
|
if n == 0:
|
||||||
|
raise ValueError("Could not patch @default uiScale in Forge_MZ.js")
|
||||||
|
text = re.sub(r"\(P\.uiScale \|\| 'auto'\)", f"(P.uiScale || '{scale}')", text)
|
||||||
|
return text
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,8 @@ def install(game_root: Path, source_js: Path | None = None, cfg: dict | None = N
|
||||||
target.write_text(prepare_forge_mz_js(source_js, cfg), encoding="utf-8")
|
target.write_text(prepare_forge_mz_js(source_js, cfg), encoding="utf-8")
|
||||||
|
|
||||||
hotkey = (cfg or {}).get("forgeHotkey", "F10")
|
hotkey = (cfg or {}).get("forgeHotkey", "F10")
|
||||||
entry = plugin_entry(hotkey)
|
ui_scale = (cfg or {}).get("uiScale", "auto")
|
||||||
|
entry = plugin_entry(hotkey, ui_scale)
|
||||||
if not _is_declared(content):
|
if not _is_declared(content):
|
||||||
idx = content.rfind("];")
|
idx = content.rfind("];")
|
||||||
if idx < 0:
|
if idx < 0:
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,13 @@ from dotenv import dotenv_values
|
||||||
|
|
||||||
ENV_TL_HOTKEY = "tlHotkey"
|
ENV_TL_HOTKEY = "tlHotkey"
|
||||||
ENV_FORGE_HOTKEY = "forgeHotkey"
|
ENV_FORGE_HOTKEY = "forgeHotkey"
|
||||||
|
ENV_UI_SCALE = "playtestUiScale"
|
||||||
ENV_EDITOR = "tlEditorCmd"
|
ENV_EDITOR = "tlEditorCmd"
|
||||||
|
|
||||||
DEFAULTS = {
|
DEFAULTS = {
|
||||||
"hotkey": "F9",
|
"hotkey": "F9",
|
||||||
"forgeHotkey": "F10",
|
"forgeHotkey": "F10",
|
||||||
|
"uiScale": "auto",
|
||||||
"editorCmd": "auto",
|
"editorCmd": "auto",
|
||||||
"workspaceFolder": "auto",
|
"workspaceFolder": "auto",
|
||||||
}
|
}
|
||||||
|
|
@ -28,6 +30,7 @@ def load_config(env_path: Path | None = None) -> dict:
|
||||||
for key, env_key in (
|
for key, env_key in (
|
||||||
("hotkey", ENV_TL_HOTKEY),
|
("hotkey", ENV_TL_HOTKEY),
|
||||||
("forgeHotkey", ENV_FORGE_HOTKEY),
|
("forgeHotkey", ENV_FORGE_HOTKEY),
|
||||||
|
("uiScale", ENV_UI_SCALE),
|
||||||
("editorCmd", ENV_EDITOR),
|
("editorCmd", ENV_EDITOR),
|
||||||
):
|
):
|
||||||
val = (env.get(env_key) or "").strip()
|
val = (env.get(env_key) or "").strip()
|
||||||
|
|
@ -42,6 +45,7 @@ def save_config(cfg: dict, env_path: Path | None = None) -> None:
|
||||||
updates = {
|
updates = {
|
||||||
ENV_TL_HOTKEY: cfg.get("hotkey", DEFAULTS["hotkey"]),
|
ENV_TL_HOTKEY: cfg.get("hotkey", DEFAULTS["hotkey"]),
|
||||||
ENV_FORGE_HOTKEY: cfg.get("forgeHotkey", DEFAULTS["forgeHotkey"]),
|
ENV_FORGE_HOTKEY: cfg.get("forgeHotkey", DEFAULTS["forgeHotkey"]),
|
||||||
|
ENV_UI_SCALE: cfg.get("uiScale", DEFAULTS["uiScale"]),
|
||||||
ENV_EDITOR: cfg.get("editorCmd", DEFAULTS["editorCmd"]),
|
ENV_EDITOR: cfg.get("editorCmd", DEFAULTS["editorCmd"]),
|
||||||
}
|
}
|
||||||
for key, val in updates.items():
|
for key, val in updates.items():
|
||||||
|
|
|
||||||
|
|
@ -72,11 +72,44 @@
|
||||||
historySize: 80, // how many past text lines to keep
|
historySize: 80, // how many past text lines to keep
|
||||||
captureUiText: true, // capture plugin / menu / UI text drawn via Bitmap.drawText
|
captureUiText: true, // capture plugin / menu / UI text drawn via Bitmap.drawText
|
||||||
// (so e.g. status-window strings from plugins.js are locatable)
|
// (so e.g. status-window strings from plugins.js are locatable)
|
||||||
dataDirOverride: null // set an absolute path to force the data dir
|
dataDirOverride: null, // set an absolute path to force the data dir
|
||||||
|
uiScale: 'auto' // overlay scale: 'auto' from game width, or a number (1.5, 2, …)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!CFG.enabled) { return; }
|
if (!CFG.enabled) { return; }
|
||||||
|
|
||||||
|
function resolveUiScale(v) {
|
||||||
|
if (v !== 'auto' && v != null && String(v).trim() !== '') {
|
||||||
|
var n = parseFloat(v);
|
||||||
|
if (!isNaN(n) && n > 0) { return Math.max(0.75, Math.min(3, n)); }
|
||||||
|
}
|
||||||
|
var base = 816;
|
||||||
|
var w = (typeof Graphics !== 'undefined' && Graphics.width) ? Graphics.width :
|
||||||
|
(window.innerWidth || base);
|
||||||
|
var scale = w / base;
|
||||||
|
var dpr = window.devicePixelRatio || 1;
|
||||||
|
if (dpr > 1.15) { scale *= Math.min(2, 0.75 + dpr * 0.35); }
|
||||||
|
return Math.max(1, Math.min(2.75, scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentUiScale() {
|
||||||
|
return resolveUiScale(CFG.uiScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyOverlayScale() {
|
||||||
|
var s = currentUiScale();
|
||||||
|
var z = (s === 1) ? '' : String(s);
|
||||||
|
if (ui.root) {
|
||||||
|
ui.root.style.zoom = z;
|
||||||
|
ui.root.style.transformOrigin = (ui.side === 'left') ? 'top left' : 'top right';
|
||||||
|
}
|
||||||
|
if (editor.root) {
|
||||||
|
editor.root.style.zoom = z;
|
||||||
|
editor.root.style.transformOrigin = 'top center';
|
||||||
|
}
|
||||||
|
if (ui.pickLabel) { ui.pickLabel.style.zoom = z; }
|
||||||
|
}
|
||||||
|
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
// Node / environment bootstrap (NW.js exposes require)
|
// Node / environment bootstrap (NW.js exposes require)
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
|
@ -1339,6 +1372,7 @@
|
||||||
editor.tabsEl = tabs;
|
editor.tabsEl = tabs;
|
||||||
editor.findEl = find;
|
editor.findEl = find;
|
||||||
editor.statusEl = foot.querySelector('.tl-ed-status');
|
editor.statusEl = foot.querySelector('.tl-ed-status');
|
||||||
|
applyOverlayScale();
|
||||||
}
|
}
|
||||||
|
|
||||||
//--- documents / tabs ----------------------------------------------------
|
//--- documents / tabs ----------------------------------------------------
|
||||||
|
|
@ -1781,6 +1815,7 @@
|
||||||
ui.root.style.left = left ? '0' : 'auto';
|
ui.root.style.left = left ? '0' : 'auto';
|
||||||
ui.root.style.right = left ? 'auto' : '0';
|
ui.root.style.right = left ? 'auto' : '0';
|
||||||
ui.root.style.boxShadow = (left ? '2px' : '-2px') + ' 0 12px rgba(0,0,0,0.6)';
|
ui.root.style.boxShadow = (left ? '2px' : '-2px') + ' 0 12px rgba(0,0,0,0.6)';
|
||||||
|
applyOverlayScale();
|
||||||
}
|
}
|
||||||
|
|
||||||
function flipSide() {
|
function flipSide() {
|
||||||
|
|
@ -1941,6 +1976,7 @@
|
||||||
hlz.style.cssText = 'position:fixed;left:0;top:0;z-index:2147483645;pointer-events:none;display:none';
|
hlz.style.cssText = 'position:fixed;left:0;top:0;z-index:2147483645;pointer-events:none;display:none';
|
||||||
document.body.appendChild(hlz);
|
document.body.appendChild(hlz);
|
||||||
ui.hlLayer = hlz;
|
ui.hlLayer = hlz;
|
||||||
|
applyOverlayScale();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTabStyles() {
|
function setTabStyles() {
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,13 @@ from util.playtest.config import load_config as _load_playtest_config, save_conf
|
||||||
|
|
||||||
ENV_EDITOR = "tlEditorCmd"
|
ENV_EDITOR = "tlEditorCmd"
|
||||||
|
|
||||||
CFG_KEYS = ("editorCmd", "workspaceFolder", "hotkey")
|
CFG_KEYS = ("editorCmd", "workspaceFolder", "hotkey", "uiScale")
|
||||||
|
|
||||||
DEFAULTS = {
|
DEFAULTS = {
|
||||||
"editorCmd": "auto",
|
"editorCmd": "auto",
|
||||||
"workspaceFolder": "auto",
|
"workspaceFolder": "auto",
|
||||||
"hotkey": "F9",
|
"hotkey": "F9",
|
||||||
|
"uiScale": "auto",
|
||||||
}
|
}
|
||||||
|
|
||||||
_PKG_ROOT = Path(__file__).resolve().parent
|
_PKG_ROOT = Path(__file__).resolve().parent
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue