diff --git a/src/gui/src/UI/UIWindow.js b/src/gui/src/UI/UIWindow.js index 6573b7df5..74ca752ee 100644 --- a/src/gui/src/UI/UIWindow.js +++ b/src/gui/src/UI/UIWindow.js @@ -3904,6 +3904,16 @@ $.fn.showWindow = async function (options) { // show window const el_window = this; + // Stay-on-top windows (every fullpage/dashboard app window) are + // CREATED in the 99999999+ z band; restore must re-raise them + // into that same band. A plain counter z would demote the window + // below whatever gets focused next — and focusWindow deliberately + // never raises stay_on_top windows, so the demotion would stick + // (a dashboard-mode app would sit buried under the dashboard). + const raised_zindex = () => ($(el_window).attr('data-stay_on_top') === 'true' + ? 99999999 + (++window.last_window_zindex) + : ++window.last_window_zindex); + // A window minimized with no taskbar to animate toward (dashboard // mode) was hidden in place by hideWindow — its geometry was // never disturbed. If the app's tile is visible on the Apps tab's @@ -3917,7 +3927,7 @@ $.fn.showWindow = async function (options) { 'data-is_minimized': false, 'data-minimized_in_place': '0', }); - $(el_window).css('z-index', ++window.last_window_zindex); + $(el_window).css('z-index', raised_zindex()); const reduce_motion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; // Skip the morph while another morph still owns the window's @@ -3946,9 +3956,10 @@ $.fn.showWindow = async function (options) { if ( ! morphed && was_hidden ) $(el_window).hide(); } if ( ! morphed ) $(el_window).fadeIn(150); - // Re-introduce the control pill on restore (headless - // dashboard windows only; no-op otherwise). - el_window._dashboard_pill_flash?.(); + // Restores come back with the control pill collapsed — + // whatever state it was minimized in (headless dashboard + // windows only; no-op otherwise). + el_window._dashboard_pill_collapse?.(); // A restore re-claims the URL for this app — except when // the restore was DRIVEN by a history traversal (popstate // passes no_history: the entry is already current). @@ -3970,7 +3981,7 @@ $.fn.showWindow = async function (options) { width: `${$(el_window).attr('data-orig-width') }px`, height: `${$(el_window).attr('data-orig-height') }px`, }); - $(el_window).css('z-index', ++window.last_window_zindex); + $(el_window).css('z-index', raised_zindex()); $(el_window).attr({ 'data-is_minimized': false, @@ -4296,8 +4307,8 @@ function attach_dashboard_app_pill (el_window, options) { clearTimeout(collapse_timer); collapse_timer = setTimeout(collapse, ms); }; - // Expand + auto-collapse: played on open and again on restore, so the - // controls introduce themselves without permanently costing pixels. + // Expand + auto-collapse: played once on open, so the controls + // introduce themselves without permanently costing pixels. const flash = () => { expand(); schedule_collapse(2600); @@ -4311,6 +4322,16 @@ function attach_dashboard_app_pill (el_window, options) { $pill.on('focusin', () => expand()); $pill.on('focusout', () => schedule_collapse(1100)); + // Pressing the pill activates its window, as pressing a titlebar + // would — the pill took over the head's job. Deferred a tick because + // the document-level activation handler (initgui's mousedown → + // mouseover_window) runs after this one on hover bookkeeping that only + // mousemove refreshes; a tap with no mousemove since a dashboard click + // (touch, restored windows) would re-raise the dashboard OVER the app. + $pill.on('mousedown', () => { + setTimeout(() => $(el_window).focusWindow(), 0); + }); + // A click anywhere on the collapsed pill (incl. its invisible touch // halo) expands it; the toggle also collapses an expanded pill for an // explicit dismiss. Buttons are pointer-events:none while collapsed, @@ -4344,8 +4365,11 @@ function attach_dashboard_app_pill (el_window, options) { $(el_window).close(); }); - // showWindow re-plays the intro when the window is restored. - el_window._dashboard_pill_flash = flash; + // showWindow forces the pill shut when the window is restored — the + // intro already ran on open, and a restore should bring back the app, + // not the chrome. (Minimize collapses too, but the Back-button path + // hides the window without touching the pill.) + el_window._dashboard_pill_collapse = collapse; $(el_window).append($pill); flash(); diff --git a/src/gui/src/css/dashboard.css b/src/gui/src/css/dashboard.css index 92337448c..8575a70f0 100644 --- a/src/gui/src/css/dashboard.css +++ b/src/gui/src/css/dashboard.css @@ -4334,14 +4334,17 @@ body.dashboard-mode .window-dashboard-headless .window-body-app { text-overflow: ellipsis; /* FIXED title box (not a cap): every app's pill is the same width, so the minimize/close buttons sit at the same screen position in every - app — they're operated by muscle memory. Short titles center in - the box like a classic titlebar. The collapse animation still - drives max-width to 0. */ + app — they're operated by muscle memory. Left-aligned against the + icon: the pill is asymmetric (one icon left, two buttons right), so + a centered title lands off the pill's true center and reads as + misaligned. Icon + title cluster as the identity on the left, the + controls cluster on the right. The collapse animation still drives + max-width to 0. */ flex: none; width: 150px; max-width: 150px; - text-align: center; - margin: 0 6px 0 4px; + text-align: left; + margin: 0 6px 0 8px; transition: max-width 0.22s ease, opacity 0.15s ease, margin 0.22s ease; }