From a5214111a9426d8d5aadd03926c5c6ac2cd140c1 Mon Sep 17 00:00:00 2001 From: Sergey Stepanov Date: Mon, 11 Mar 2024 15:24:48 +0300 Subject: [PATCH] Update OPTIONS UI --- web/css/main.css | 2 +- web/css/ui.css | 109 ++++++++-------------------- web/index.html | 22 ++---- web/js/gui/gui.js | 8 ++- web/js/settings/settings.js | 138 ++++++++++++++++++------------------ 5 files changed, 112 insertions(+), 167 deletions(-) diff --git a/web/css/main.css b/web/css/main.css index 10d98e2eb..270b7ce81 100644 --- a/web/css/main.css +++ b/web/css/main.css @@ -40,7 +40,7 @@ body { background-image: url('/img/ui/bg.jpg'); background-repeat: no-repeat; background-size: 100% 100%; - border-radius: 22px; + border-radius: 24px; user-select: none; } diff --git a/web/css/ui.css b/web/css/ui.css index 41f70879c..334d5e756 100644 --- a/web/css/ui.css +++ b/web/css/ui.css @@ -3,67 +3,6 @@ display: none !important; } -.modal-window { - position: fixed; - - top: 0; - right: 0; - bottom: 0; - left: 0; - - background-color: rgba(0, 0, 0, 0.7); - - z-index: 9999; - visibility: hidden; - opacity: 0; - pointer-events: none; - - -webkit-transition: all 0.2s; - transition: all 0.2s; -} - -.modal-visible { - visibility: visible; - opacity: 1; - pointer-events: auto; -} - - -.modal-window > div { - width: 42vw; - position: absolute; - top: 50%; - left: 50%; - -webkit-transform: translate(-50%, -50%); - transform: translate(-50%, -50%); - padding: 2em; - background: #ffffff; -} - -.modal-window header { - font-weight: bold; -} - -.modal-window h1 { - font-size: 150%; - margin: 0 0 15px; -} - -.semi-button { - cursor: pointer; -} - - -#app-settings { - font-family: monospace; -} - -#settings-data { - overflow-y: auto; - height: 50vh; - padding: 1em 0; -} - .container { display: grid; -webkit-box-pack: center; @@ -73,10 +12,6 @@ height: 100vh; } -.modal-window div:not(:last-of-type) { - margin-bottom: 15px; -} - .btn2 { font-size: 80%; padding: .2em .4em; @@ -88,6 +23,10 @@ height: 1rem; } +.settings { + padding: 0 1em 1em 1em; +} + .settings__controls { color: #aaa; font-size: 80%; @@ -106,7 +45,7 @@ .settings__option-name { background-color: beige; - padding: 1em; + margin: 1em 0 .5em 0; } .restart-needed-asterisk:after { @@ -114,13 +53,15 @@ color: red; } -.settings__option-value { - +.settings__option-value select, .settings__option-value input { + font-family: '6809', monospace; + width: 6em; } .keyboard-bindings .settings__option-value { display: grid; grid-template-columns: 25% 25% auto auto; + row-gap: 5px; } .binding-element { @@ -129,19 +70,17 @@ align-items: center; } -/* Server list styling */ -#servers { - background-color: white; - font-size: 12px; - +.binding-element button { font-family: '6809', monospace; + min-width: 6em; +} - z-index: 1; - position: relative; - - cursor: default; +.binding-element div { + font-size: 80%; } +/* Server list styling */ + .server-list div { display: grid; grid-template-columns: .2fr 1.2fr 1fr .5fr .2fr; @@ -169,6 +108,16 @@ display: flex; flex-grow: 1; flex-direction: column; + + background-color: white; + font-size: 12px; + + font-family: '6809', monospace; + + z-index: 1; + position: relative; + + cursor: default; } .panel__header { @@ -209,7 +158,7 @@ background-color: #ededed; padding: 2px 4px; - width: 0.7rem; + min-width: 0.7rem; text-align: center; } @@ -224,6 +173,10 @@ font-weight: bold; } +.panel__button_separator { + width: .5em; +} + .app-button { position: absolute; diff --git a/web/index.html b/web/index.html index 0c40aba5f..2953032d4 100644 --- a/web/index.html +++ b/web/index.html @@ -15,8 +15,8 @@ - - + + Cloud Retro @@ -46,6 +46,7 @@
+
Arrows (move), ZXCVAS;'./ (game ABXYL1-L3R1-R3), 1/2 (1st/2nd player), Shift/Enter/K/L (select/start/save/load), F (fullscreen), share (copy the link to the clipboard) @@ -94,21 +95,6 @@
{{end}}
-
69ff8ae @@ -116,7 +102,7 @@

Options

- + diff --git a/web/js/gui/gui.js b/web/js/gui/gui.js index be5b603f3..c83dc9480 100644 --- a/web/js/gui/gui.js +++ b/web/js/gui/gui.js @@ -26,8 +26,7 @@ const gui = (() => { return el; } - const select = (key = '', callback = function () { - }, values = {values: [], labels: []}, current = '') => { + const select = (key = '', callback = () => ({}), values = {values: [], labels: []}, current = '') => { const el = _create(); const select = _create('select'); select.onchange = event => { @@ -70,6 +69,10 @@ const gui = (() => { el.classList.add('panel__header__controls'); buttons.forEach((b => el.append(_create('span', (el) => { + if (Object.keys(b).length === 0) { + el.classList.add('panel__button_separator'); + return + } el.classList.add('panel__button'); if (b.cl) b.cl.forEach(class_ => el.classList.add(class_)); if (b.title) el.title = b.title; @@ -110,6 +113,7 @@ const gui = (() => { } return { + contentEl: _content, isHidden: () => !state.shown, setContent, setLoad, diff --git a/web/js/settings/settings.js b/web/js/settings/settings.js index 0e67fdd71..15f459df9 100644 --- a/web/js/settings/settings.js +++ b/web/js/settings/settings.js @@ -46,17 +46,7 @@ const settings = (() => { const exportFileName = `cloud-game.settings.v${revision}.txt`; - // ui references - const ui = document.getElementById('app-settings'), - closeEl = document.getElementById('settings__controls__close'), - loadEl = document.getElementById('settings__controls__load'), - saveEl = document.getElementById('settings__controls__save'), - resetEl = document.getElementById('settings__controls__reset'); - - this._renderrer = this._renderrer || { - render: () => { - } - }; + let _renderer = {render: () => ({})}; const getStore = () => store.settings; @@ -64,8 +54,7 @@ const settings = (() => { * The NullObject provider if everything else fails. */ const voidProvider = (store_ = {settings: {}}) => { - const nil = () => { - } + const nil = () => ({}) return { get: key => store_.settings[key], @@ -107,7 +96,7 @@ const settings = (() => { const get = key => JSON.parse(localStorage.getItem(key)); - const set = (key, value) => save(); + const set = () => save(); const remove = () => save(); @@ -161,7 +150,6 @@ const settings = (() => { document.body.appendChild(el); el.click(); document.body.removeChild(el); - el = undefined; } const init = () => { @@ -256,13 +244,47 @@ const settings = (() => { provider.remove(key, subKey); } - const _render = () => settings._renderrer.render() + const panel = gui.panel(document.getElementById('settings'), 'OPTIONS', 'settings', null, [ + {caption: 'Export', handler: () => _export(), title: 'Save',}, + {caption: 'Import', handler: () => _fileReader.read(onFileLoad), title: 'Load',}, + { + caption: 'Reset', + handler: () => { + if (window.confirm("Are you sure want to reset your settings?")) { + _reset(); + event.pub(SETTINGS_CHANGED); + } + }, + title: 'Reset', + }, + {} + ], + // hack not transparent jpeg corners :_; + ((br) => (state, el) => { + state ? el.parentElement.style.borderRadius = '0px' : + br ? el.parentElement.style.borderRadius = br : + br = window.getComputedStyle(el.parentElement).borderRadius + + if (!state) { + event.pub(SETTINGS_CLOSED); + // to make sure it's disabled, but it's a tad verbose + event.pub(KEYBOARD_TOGGLE_FILTER_MODE, {mode: true}); + } + })()) + - /** - * Settings modal window toggle handler. - * @returns {boolean} True in case if it's opened. - */ - const toggle = () => ui.classList.toggle('modal-visible') && !_render(); + panel.toggle(false); + + + const _render = () => { + _renderer.data = panel.contentEl; + _renderer.render() + } + + const toggle = () => { + panel.toggle(true); + _render() + } function _getType(value) { if (value === undefined) return option.undefined @@ -273,15 +295,8 @@ const settings = (() => { else return option.undefined; } - /** - * File reader submodule (FileReader API). - * - * @type {{read: read}} Tries to read a file. - * @private - */ const _fileReader = (() => { - let callback_ = () => { - } + let callback_ = () => ({}) const el = document.createElement('input'); const reader = new FileReader(); @@ -309,21 +324,6 @@ const settings = (() => { event.sub(SETTINGS_CHANGED, _render); - // internal init section - closeEl.addEventListener('click', () => { - event.pub(SETTINGS_CLOSED); - // to make sure it's disabled, but it's a tad verbose - event.pub(KEYBOARD_TOGGLE_FILTER_MODE, {mode: true}); - }); - saveEl.addEventListener('click', () => _export()); - loadEl.addEventListener('click', () => _fileReader.read(onFileLoad)); - resetEl.addEventListener('click', () => { - if (window.confirm("Are you sure want to reset your settings?")) { - _reset(); - event.pub(SETTINGS_CHANGED); - } - }); - return { init, loadOr, @@ -335,24 +335,31 @@ const settings = (() => { export: _export, ui: { toggle, + }, + set renderer(fn) { + _renderer = fn; } } })(document, event, JSON, localStorage, log, window); // hardcoded ui stuff -settings._renderrer = (() => { +settings.renderer = (() => { // options to ignore (i.e. ignored = {'_version': 1}) const ignored = {}; // the main display data holder element - const data = document.getElementById('settings-data'); - - let sx, sy = 0; - - data.addEventListener("scroll", event => { - sx = data.scrollTop; - sy = data.scrollLeft; - }, {passive: true}); + let data = null; + + const scrollState = ((sx = 0, sy = 0, el) => ({ + track(_el) { + el = _el + el.addEventListener("scroll", () => ({scrollTop: sx, scrollLeft: sy} = el), {passive: true}) + }, + restore() { + el.scrollTop = sx + el.scrollLeft = sy + } + }))() // a fast way to clear data holder. const clearData = () => { @@ -380,9 +387,6 @@ settings._renderrer = (() => { wrapperEl.classList.add(name); return this; }, - readOnly: function () { - // reserved - }, restartNeeded: function () { nameEl.classList.add('restart-needed-asterisk'); return this; @@ -408,11 +412,7 @@ settings._renderrer = (() => { } } - // !to check leaks - if (handler) { - handler.unsub(); - handler = undefined; - } + handler?.unsub(); event.pub(KEYBOARD_TOGGLE_FILTER_MODE); event.pub(SETTINGS_CHANGED); @@ -433,12 +433,10 @@ settings._renderrer = (() => { * * @param key The name (id) of an option. * @param newValue A new value to set. - * @param oldValue An old value to use somehow if needed. */ - const onChange = (key, newValue, oldValue) => { + const onChange = (key, newValue) => { settings.set(key, newValue); - data.scrollTop = sx; - data.scrollLeft = sy; + scrollState.restore(data); } const onKeyBindingChange = (key, oldValue) => { @@ -457,7 +455,7 @@ settings._renderrer = (() => { const value = _settings[k]; switch (k) { case opts._VERSION: - _option(data).withName('Format version').add(value).build(); + _option(data).withName().add(value).build(); break; case opts.LOG_LEVEL: _option(data).withName('Log level') @@ -474,8 +472,8 @@ settings._renderrer = (() => { .build(); break; case opts.MIRROR_SCREEN: - _option(data).withName('Video mirroring without smooth') - .add(gui.select(k, onChange, {values: ['mirror']}, value)) + _option(data).withName('Video mirroring aka disable video image smoothing (slow)') + .add(gui.select(k, onChange, {values: ['mirror'], labels: []}, value)) .build(); break; case opts.VOLUME: @@ -492,5 +490,9 @@ settings._renderrer = (() => { return { render, + set data(el) { + data = el; + scrollState.track(el) + } } })(document, log, opts, settings);