Title bar: the app bar shows no brand mark under the desktop's own frame

On Linux, when the desktop draws the window's frame (KDE, Sway,
Hyprland, COSMIC and X11 sessions), the app bar painted the brand mark
under the desktop's title bar unless System Title Bar was on. The mark
followed the live preference instead of the frame the window has.

The desktop app now records the frame it opened the window with
(VectorcraftApp::desktop_frame), and system_title_bar reads it. A
change to System Title Bar during a session no longer shows or hides
the mark; it changes with the frame at the next start. Embedders
without a window of their own keep the mark.
This commit is contained in:
dotCooCoo
2026-11-08 23:50:37 +07:00
committed by bflatastic
parent 2049082ce7
commit 8c7e7041fd
6 changed files with 51 additions and 7 deletions
+1
View File
@@ -467,6 +467,7 @@ fn main() -> std::process::ExitCode {
rs.device.set_device_lost_callback(move |reason, msg| loss.report(&ctx, format!("{reason:?}: {msg}")));
}
app.custom_titlebar = custom_titlebar;
app.desktop_frame = !custom_titlebar;
if let Some(port) = control_port {
let rx = control_server::start(port, cc.egui_ctx.clone());
app = app.with_control(rx).with_automation_roots(roots.clone());
+1
View File
@@ -123,6 +123,7 @@ impl VectorcraftApp {
canvas_rect: None,
hover_doc: None,
custom_titlebar: false,
desktop_frame: false,
last_window_title: String::new(),
fullscreen_for_mode: false,
graphics_adapter: None,
+8 -6
View File
@@ -129,13 +129,15 @@ pub fn app_bar(app: &mut VectorcraftApp, ui: &mut Ui) {
}
}
/// Whether the window shows the system's title bar instead of the app drawing its own: Windows
/// and Linux with Preferences › User Interface › System Title Bar. macOS always has system
/// decorations, but keeps the in-app brand mark, so it never counts as system mode here; nor
/// does the web build (the browser tab has no document title), nor an embedder that never
/// turned the preference on.
/// Whether the window shows the system's title bar instead of the app drawing its own
/// ([`VectorcraftApp::desktop_frame`]): Windows with Preferences › User Interface › System Title
/// Bar, and Linux whenever the desktop draws the frame, with or without the preference. The
/// preference is read when the app starts, so changing it later does not change this. macOS
/// always has system decorations, but keeps the in-app brand mark, so it never counts as system
/// mode here; nor does the web build (the browser tab has no document title), nor an embedder
/// without a window of its own.
pub fn system_title_bar(app: &VectorcraftApp) -> bool {
!app.custom_titlebar && app.session.prefs.system_title_bar && !cfg!(any(target_os = "macos", target_arch = "wasm32"))
app.desktop_frame && !app.custom_titlebar && !cfg!(any(target_os = "macos", target_arch = "wasm32"))
}
/// The OS window title: the active document's name suffixed with the app name (a `*` prefix
+35
View File
@@ -1230,16 +1230,51 @@ fn the_zoom_hint_follows_animated_zoom() {
fn title_test_app(custom_titlebar: bool) -> crate::VectorcraftApp {
let mut app = crate::VectorcraftApp::new(Session::new(), Default::default());
app.custom_titlebar = custom_titlebar;
app.desktop_frame = !custom_titlebar;
// The preference that makes the shell launch without its own title bar.
app.run("prefs.set", json!({"key": "systemTitleBar", "value": !custom_titlebar})).unwrap();
app
}
/// How many brand marks one frame of the app bar paints.
fn brand_marks(app: &mut crate::VectorcraftApp) -> usize {
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| super::app_bar(app, ui));
out.textures_delta.clear();
out.shapes.iter().filter(|c| matches!(&c.shape, egui::Shape::Mesh(m) if m.texture_id == crate::brand::texture_id(&ctx))).count()
}
#[test]
fn without_the_preference_the_mark_stays() {
// The default (e.g. the web build): no custom bar, no preference.
let app = crate::VectorcraftApp::new(Session::new(), Default::default());
assert!(!super::system_title_bar(&app));
// An embedder without a window of its own keeps the mark with the preference on too.
let mut app = crate::VectorcraftApp::new(Session::new(), Default::default());
app.run("prefs.set", json!({"key": "systemTitleBar", "value": true})).unwrap();
assert!(!super::system_title_bar(&app));
assert_eq!(brand_marks(&mut app), 1);
}
#[test]
fn a_desktop_frame_hides_the_mark_whatever_the_preference_says() {
// Linux on a desktop that draws the frame (KDE, X11): the window has the desktop's title bar
// with System Title Bar off.
let mut app = crate::VectorcraftApp::new(Session::new(), Default::default());
app.desktop_frame = true;
app.run("prefs.set", json!({"key": "systemTitleBar", "value": false})).unwrap();
let hidden = !cfg!(target_os = "macos");
assert_eq!(super::system_title_bar(&app), hidden);
assert_eq!(brand_marks(&mut app), if hidden { 0 } else { 1 });
// The preference applies at the next start: changing it now leaves the window's frame, and the mark, as they are.
let mut app = title_test_app(false);
app.run("prefs.set", json!({"key": "systemTitleBar", "value": false})).unwrap();
assert_eq!(super::system_title_bar(&app), hidden);
let mut app = title_test_app(true);
app.run("prefs.set", json!({"key": "systemTitleBar", "value": true})).unwrap();
assert!(!super::system_title_bar(&app));
assert_eq!(brand_marks(&mut app), 1);
}
#[test]
+4
View File
@@ -198,6 +198,10 @@ pub struct VectorcraftApp {
/// Windows and Linux: the window has no OS decorations, so the app bar is the title bar (drag,
/// double-click to maximize, caption buttons) and invisible edge zones resize the window.
pub custom_titlebar: bool,
/// The window has the desktop's own title bar: the OS or the window manager draws its frame,
/// icon and title. Set by the desktop app when it opens the window; `false` for embedders
/// without a window of their own.
pub desktop_frame: bool,
/// Last window title sent to the OS (`ViewportCommand::Title`, see `chrome::sync_window_title`):
/// sent again only when it changes, so idle frames don't spam the backend.
pub(crate) last_window_title: String,
+2 -1
View File
@@ -328,7 +328,8 @@ not commands, so drive them with `ui.click` if needed. Close runs `app.quit` (th
modified documents), and empty bar space and the 5 pt window edges move and resize the window. Preferences › User
Interface › System Title Bar (`systemTitleBar`, read when the app starts) gives the window its OS decorations back
instead: the OS title then shows the active document (`*name — VectorCraft` with unsaved changes) and the app bar
drops its brand mark. On macOS the window buttons are centred on the app bar; the web build is unchanged.
drops its brand mark. On Linux the app bar also drops its brand mark whenever the desktop draws the window's frame, with
System Title Bar on or off. On macOS the window buttons are centred on the app bar; the web build is unchanged.
Fill/Stroke chips and panel shortcuts: the Control bar's and Properties' Fill and Stroke chips bring their proxy
forward (`paint.toggleActive {fill}`) and open a popover with the Swatches panel (Shift-click: the Color panel's mixer);