Dashboard: fix minimized apps getting permanently buried under the dashboard (#3429)

* Dashboard: fix minimized apps getting permanently buried under the dashboard

Fullpage/dashboard app windows are created stay_on_top in the
99999999+ z band, but showWindow's restore path demoted them to a
plain counter z-index — and since focusWindow deliberately never
raises stay_on_top windows, the demotion stuck: one dashboard focus
after a minimize/restore buried the app permanently (URL and tab
title still naming an app the user could no longer see). Restore now
re-raises stay-on-top windows into their band.

Also make the control pill focus its window on mousedown, as the
titlebar it replaces did. The document-level activation handler works
off mouseover_window, which only mousemove refreshes — a tap with no
intervening mousemove (touch, restored windows) would activate the
stale window and could re-raise the dashboard over the app. Deferred
a tick so it runs after that handler and wins.

* Dashboard: restored windows come back with the pill collapsed

The restore re-played the open intro (expand + auto-retract). A restore
should bring back the app, not the chrome — the user already met the
controls on open, and they just USED them to minimize. showWindow now
forces the pill shut instead, which also covers the Back-button
minimize path, where the window hides without touching pill state.

* Dashboard pill: left-align the title against the icon

The pill is asymmetric — one icon on the left, two buttons on the
right — so a title centered in its fixed box landed off the pill's
true center and read as misaligned. Left-aligned, icon + title
cluster as the identity on the left and the controls cluster on
the right.
This commit is contained in:
Nariman Jelveh
2026-07-22 21:40:04 -07:00
committed by GitHub
parent e35b57068b
commit bbc86e7b70
2 changed files with 41 additions and 14 deletions
+33 -9
View File
@@ -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();
+8 -5
View File
@@ -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;
}