revert: roll back titlebar-mode-selector merge
Reverts merge 1be46f1a which introduced the 4-mode titlebar enum
(qbz/system/plasma/hidden), variant-aware rendering (full/stripped),
GDK_BACKEND=x11 coercion for Plasma, and per-mode mounting in +page.
The "stripped strip mounted alongside Plasma SSD" architecture caused
all titlebar modes to render incorrectly in the production binary —
empty space at top, separation gap above the now-playing bar, controls
visible without a backing strip. Reverting restores v1.2.10 behavior:
two booleans (use_system_titlebar + hide_title_bar), no variants, no
GDK coercion. Plasma support is dropped pending a less brittle redesign.
Conflicts in SettingsView.svelte resolved by taking the pre-merge side
for the four titlebar UI blocks; non-titlebar additions in this cycle
(Discord RPC settings, reserve-DAC, offline cache nav) preserved.
cc
This commit is contained in:
16 files changed
+287
-782
No files matched your search
@@ -10,7 +10,6 @@ use crate::config::playback_preferences::{
|
||||
use crate::config::tray_settings::TraySettings;
|
||||
use crate::config::tray_settings::TraySettingsState;
|
||||
use crate::config::window_settings::WindowSettingsState;
|
||||
use crate::config::window_settings::{available_titlebar_modes, is_sandboxed, TitlebarMode};
|
||||
use crate::core_bridge::CoreBridgeState;
|
||||
use crate::AppState;
|
||||
|
||||
@@ -24,32 +23,11 @@ pub async fn v2_set_api_locale(locale: String, state: State<'_, AppState>) -> Re
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn v2_get_titlebar_mode(
|
||||
state: State<'_, WindowSettingsState>,
|
||||
) -> Result<TitlebarMode, String> {
|
||||
state.get_titlebar_mode()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn v2_set_titlebar_mode(
|
||||
mode: TitlebarMode,
|
||||
pub fn v2_set_use_system_titlebar(
|
||||
value: bool,
|
||||
state: State<'_, WindowSettingsState>,
|
||||
) -> Result<(), String> {
|
||||
state.set_titlebar_mode(mode)
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct TitlebarModeAvailability {
|
||||
pub modes: Vec<TitlebarMode>,
|
||||
pub sandboxed: bool,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn v2_available_titlebar_modes() -> TitlebarModeAvailability {
|
||||
TitlebarModeAvailability {
|
||||
modes: available_titlebar_modes(),
|
||||
sandboxed: is_sandboxed(),
|
||||
}
|
||||
state.set_use_system_titlebar(value)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
@@ -9,44 +9,9 @@ use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Titlebar rendering mode. Controls which decoration source is active and
|
||||
/// how the custom titlebar component renders (or whether it renders at all).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum TitlebarMode {
|
||||
/// Default — full custom titlebar with drag, resize, dblclick, controls.
|
||||
Qbz,
|
||||
/// OS-native chrome (CSD on Wayland, KWin SSD on X11, NSWindow on macOS).
|
||||
System,
|
||||
/// KDE-only — KWin SSD via Xwayland + stripped custom strip below for search+nav.
|
||||
Plasma,
|
||||
/// No titlebar — for tiling WMs (i3/Hyprland/sway/niri).
|
||||
Hidden,
|
||||
}
|
||||
|
||||
impl TitlebarMode {
|
||||
/// Whether this mode requests OS decorations (decorations=true at window creation).
|
||||
pub fn wants_decorations(self) -> bool {
|
||||
matches!(self, TitlebarMode::System | TitlebarMode::Plasma)
|
||||
}
|
||||
|
||||
/// Whether this mode requires GDK_BACKEND=x11 to coerce SSD on Wayland.
|
||||
pub fn wants_xwayland(self) -> bool {
|
||||
matches!(self, TitlebarMode::Plasma)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TitlebarMode {
|
||||
fn default() -> Self {
|
||||
TitlebarMode::Qbz
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WindowSettings {
|
||||
/// Use OS native window decorations (legacy — superseded by `titlebar_mode`).
|
||||
/// Kept as a write-through shadow of `titlebar_mode in {System, Plasma}` for
|
||||
/// any consumers that still read it; will be removed in a follow-up.
|
||||
/// Use OS native window decorations instead of custom title bar
|
||||
pub use_system_titlebar: bool,
|
||||
/// Last saved window width (logical pixels, non-maximized)
|
||||
pub window_width: f64,
|
||||
@@ -58,8 +23,6 @@ pub struct WindowSettings {
|
||||
/// Implies building the window transparent on Linux so the corners
|
||||
/// can be seen through to the desktop. Requires restart to take effect.
|
||||
pub match_system_window_chrome: bool,
|
||||
/// Authoritative titlebar mode (replaces `use_system_titlebar`).
|
||||
pub titlebar_mode: TitlebarMode,
|
||||
}
|
||||
|
||||
impl Default for WindowSettings {
|
||||
@@ -70,7 +33,6 @@ impl Default for WindowSettings {
|
||||
window_height: 800.0,
|
||||
is_maximized: false,
|
||||
match_system_window_chrome: false,
|
||||
titlebar_mode: TitlebarMode::Qbz,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,26 +86,6 @@ impl WindowSettingsStore {
|
||||
[],
|
||||
);
|
||||
|
||||
// Add titlebar_mode column. Defaults to NULL so we can detect
|
||||
// pre-existing rows and migrate from use_system_titlebar.
|
||||
let _ = conn.execute(
|
||||
"ALTER TABLE window_settings ADD COLUMN titlebar_mode TEXT",
|
||||
[],
|
||||
);
|
||||
|
||||
// One-time migration: if titlebar_mode is NULL but use_system_titlebar
|
||||
// exists, derive mode. This runs at every open but only does work on
|
||||
// rows where titlebar_mode is still NULL (idempotent).
|
||||
let _ = conn.execute(
|
||||
"UPDATE window_settings
|
||||
SET titlebar_mode = CASE
|
||||
WHEN use_system_titlebar = 1 THEN 'system'
|
||||
ELSE 'qbz'
|
||||
END
|
||||
WHERE titlebar_mode IS NULL AND id = 1",
|
||||
[],
|
||||
);
|
||||
|
||||
info!("[WindowSettings] Database initialized");
|
||||
|
||||
Ok(Self { conn })
|
||||
@@ -160,32 +102,11 @@ impl WindowSettingsStore {
|
||||
Self::open_at(base_dir, "window_settings.db")
|
||||
}
|
||||
|
||||
/// Lightweight read-only open for startup (before Tauri state).
|
||||
/// Opens existing DB without creating tables/running migrations.
|
||||
pub fn new_readonly() -> Result<Self, String> {
|
||||
let db_path = dirs::data_dir()
|
||||
.ok_or("Could not determine data directory")?
|
||||
.join("qbz")
|
||||
.join("window_settings.db");
|
||||
|
||||
if !db_path.exists() {
|
||||
return Err("Window settings DB does not exist yet".to_string());
|
||||
}
|
||||
|
||||
let conn = Connection::open_with_flags(
|
||||
&db_path,
|
||||
rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY | rusqlite::OpenFlags::SQLITE_OPEN_NO_MUTEX,
|
||||
)
|
||||
.map_err(|e| format!("Failed to open window settings database (readonly): {}", e))?;
|
||||
|
||||
Ok(Self { conn })
|
||||
}
|
||||
|
||||
pub fn get_settings(&self) -> Result<WindowSettings, String> {
|
||||
self.conn
|
||||
.query_row(
|
||||
"SELECT use_system_titlebar, window_width, window_height, is_maximized,
|
||||
match_system_window_chrome, titlebar_mode
|
||||
match_system_window_chrome
|
||||
FROM window_settings WHERE id = 1",
|
||||
[],
|
||||
|row| {
|
||||
@@ -194,13 +115,6 @@ impl WindowSettingsStore {
|
||||
let window_height: f64 = row.get(2)?;
|
||||
let is_maximized: i32 = row.get(3)?;
|
||||
let match_chrome: i32 = row.get(4)?;
|
||||
let mode_str: Option<String> = row.get(5)?;
|
||||
let titlebar_mode = match mode_str.as_deref() {
|
||||
Some("system") => TitlebarMode::System,
|
||||
Some("plasma") => TitlebarMode::Plasma,
|
||||
Some("hidden") => TitlebarMode::Hidden,
|
||||
_ => TitlebarMode::Qbz,
|
||||
};
|
||||
let defaults = WindowSettings::default();
|
||||
let (w, h) = if is_valid_window_size(window_width, window_height) {
|
||||
(window_width, window_height)
|
||||
@@ -218,7 +132,6 @@ impl WindowSettingsStore {
|
||||
window_height: h,
|
||||
is_maximized: is_maximized != 0,
|
||||
match_system_window_chrome: match_chrome != 0,
|
||||
titlebar_mode,
|
||||
})
|
||||
},
|
||||
)
|
||||
@@ -245,26 +158,6 @@ impl WindowSettingsStore {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_titlebar_mode(&self, mode: TitlebarMode) -> Result<(), String> {
|
||||
let mode_str = match mode {
|
||||
TitlebarMode::Qbz => "qbz",
|
||||
TitlebarMode::System => "system",
|
||||
TitlebarMode::Plasma => "plasma",
|
||||
TitlebarMode::Hidden => "hidden",
|
||||
};
|
||||
let use_system = mode.wants_decorations() as i64;
|
||||
self.conn
|
||||
.execute(
|
||||
"UPDATE window_settings
|
||||
SET titlebar_mode = ?1, use_system_titlebar = ?2
|
||||
WHERE id = 1",
|
||||
params![mode_str, use_system],
|
||||
)
|
||||
.map_err(|e| format!("Failed to set titlebar_mode: {}", e))?;
|
||||
info!("Window titlebar_mode set to {}", mode_str);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Save the non-maximized window dimensions (called on resize while not maximized).
|
||||
pub fn set_window_size(&self, width: f64, height: f64) -> Result<(), String> {
|
||||
if !is_valid_window_size(width, height) {
|
||||
@@ -369,23 +262,6 @@ impl WindowSettingsState {
|
||||
.ok_or("Window settings store not initialized")?;
|
||||
store.set_match_system_window_chrome(value)
|
||||
}
|
||||
|
||||
pub fn set_titlebar_mode(&self, mode: TitlebarMode) -> Result<(), String> {
|
||||
let store = self.store.lock().map_err(|e| format!("lock poisoned: {}", e))?;
|
||||
store
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Window settings store not initialized".to_string())?
|
||||
.set_titlebar_mode(mode)
|
||||
}
|
||||
|
||||
pub fn get_titlebar_mode(&self) -> Result<TitlebarMode, String> {
|
||||
let store = self.store.lock().map_err(|e| format!("lock poisoned: {}", e))?;
|
||||
let settings = store
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Window settings store not initialized".to_string())?
|
||||
.get_settings()?;
|
||||
Ok(settings.titlebar_mode)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that window dimensions are within a sane range.
|
||||
@@ -430,43 +306,3 @@ pub fn set_match_system_window_chrome(
|
||||
);
|
||||
state.set_match_system_window_chrome(value)
|
||||
}
|
||||
|
||||
/// Returns the titlebar modes valid for the current platform/session.
|
||||
/// `Plasma` is only included on KDE Plasma + Wayland — on other environments
|
||||
/// the Xwayland trick is unnecessary or non-applicable.
|
||||
pub fn available_titlebar_modes() -> Vec<TitlebarMode> {
|
||||
let mut modes = vec![
|
||||
TitlebarMode::Qbz,
|
||||
TitlebarMode::System,
|
||||
TitlebarMode::Hidden,
|
||||
];
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let kde = std::env::var("XDG_CURRENT_DESKTOP")
|
||||
.map(|d| d.split(':').any(|s| s.eq_ignore_ascii_case("KDE")))
|
||||
.unwrap_or(false);
|
||||
let wayland = std::env::var_os("WAYLAND_DISPLAY").is_some()
|
||||
|| std::env::var("XDG_SESSION_TYPE").as_deref() == Ok("wayland");
|
||||
if kde && wayland {
|
||||
modes.push(TitlebarMode::Plasma);
|
||||
}
|
||||
}
|
||||
|
||||
modes
|
||||
}
|
||||
|
||||
/// Whether the current process is running inside a Linux sandbox
|
||||
/// (Flatpak or Snap). Used by the frontend to display a sandbox-caveat
|
||||
/// hint when the user selects Plasma mode in those builds.
|
||||
pub fn is_sandboxed() -> bool {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
std::path::Path::new("/.flatpak-info").exists()
|
||||
|| std::env::var_os("SNAP").is_some()
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
false
|
||||
}
|
||||
}
|
||||
+9
-18
@@ -542,15 +542,10 @@ fn should_use_main_window_transparency() -> bool {
|
||||
|
||||
// Opt-in via persisted settings: "match system window chrome" wants
|
||||
// rounded corners, which need a transparent window so the webview
|
||||
// background stops painting white outside the radius. Only honor this
|
||||
// when the titlebar mode is Qbz — in System/Plasma/Hidden modes the
|
||||
// chrome is owned by the platform compositor or a separate widget,
|
||||
// and a transparent window leaks through KDE/system chrome.
|
||||
// background stops painting white outside the radius.
|
||||
if let Ok(store) = config::window_settings::WindowSettingsStore::new() {
|
||||
if let Ok(settings) = store.get_settings() {
|
||||
if settings.titlebar_mode == config::window_settings::TitlebarMode::Qbz
|
||||
&& settings.match_system_window_chrome
|
||||
{
|
||||
if settings.match_system_window_chrome {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -711,15 +706,14 @@ pub fn run(qconnect_cli_override: Option<bool>) {
|
||||
let window_settings = config::window_settings::WindowSettingsStore::new()
|
||||
.and_then(|store| store.get_settings())
|
||||
.unwrap_or_default();
|
||||
let titlebar_mode = window_settings.titlebar_mode;
|
||||
log::info!(
|
||||
"Window settings: titlebar_mode={:?}, decorations={}, size={}x{}, maximized={}",
|
||||
titlebar_mode,
|
||||
titlebar_mode.wants_decorations(),
|
||||
"Window settings: use_system_titlebar={}, size={}x{}, maximized={}",
|
||||
window_settings.use_system_titlebar,
|
||||
window_settings.window_width as u32,
|
||||
window_settings.window_height as u32,
|
||||
window_settings.is_maximized,
|
||||
);
|
||||
let use_system_titlebar = window_settings.use_system_titlebar;
|
||||
let mut saved_win_width = window_settings.window_width;
|
||||
let mut saved_win_height = window_settings.window_height;
|
||||
let saved_win_maximized = window_settings.is_maximized;
|
||||
@@ -950,9 +944,8 @@ pub fn run(qconnect_cli_override: Option<bool>) {
|
||||
.manage(user_data_paths)
|
||||
.setup(move |app| {
|
||||
log::info!(
|
||||
"Creating main window (mode={:?}, decorations={})",
|
||||
titlebar_mode,
|
||||
titlebar_mode.wants_decorations()
|
||||
"Creating main window (decorations={})",
|
||||
use_system_titlebar
|
||||
);
|
||||
let main_window_transparent = should_use_main_window_transparency();
|
||||
MAIN_WINDOW_TRANSPARENT.store(main_window_transparent, std::sync::atomic::Ordering::SeqCst);
|
||||
@@ -1008,7 +1001,7 @@ pub fn run(qconnect_cli_override: Option<bool>) {
|
||||
.title("QBZ")
|
||||
.inner_size(saved_win_width, saved_win_height)
|
||||
.min_inner_size(800.0, 600.0)
|
||||
.decorations(if cfg!(target_os = "macos") { true } else { titlebar_mode.wants_decorations() })
|
||||
.decorations(if cfg!(target_os = "macos") { true } else { use_system_titlebar })
|
||||
.transparent(main_window_transparent)
|
||||
.resizable(true)
|
||||
.zoom_hotkeys_enabled(true);
|
||||
@@ -1569,9 +1562,7 @@ pub fn run(qconnect_cli_override: Option<bool>) {
|
||||
updates::has_snap_welcome_been_shown,
|
||||
config::favorites_cache::is_track_favorite,
|
||||
commands_v2::v2_set_api_locale,
|
||||
commands_v2::v2_get_titlebar_mode,
|
||||
commands_v2::v2_set_titlebar_mode,
|
||||
commands_v2::v2_available_titlebar_modes,
|
||||
commands_v2::v2_set_use_system_titlebar,
|
||||
commands_v2::v2_set_match_system_window_chrome,
|
||||
commands_v2::v2_main_window_is_transparent,
|
||||
commands_v2::v2_set_enable_tray,
|
||||
|
||||
+1
-13
@@ -321,13 +321,6 @@ fn main() {
|
||||
let force_dmabuf = std::env::var("QBZ_FORCE_DMABUF").as_deref() == Ok("1");
|
||||
let disable_dmabuf = std::env::var("QBZ_DISABLE_DMABUF").as_deref() == Ok("1");
|
||||
|
||||
// Read titlebar mode from window settings store (lightweight read-only).
|
||||
let titlebar_wants_xwayland = qbz_nix_lib::config::window_settings::WindowSettingsStore::new_readonly()
|
||||
.ok()
|
||||
.and_then(|store| store.get_settings().ok())
|
||||
.map(|s| s.titlebar_mode.wants_xwayland())
|
||||
.unwrap_or(false);
|
||||
|
||||
// Force X11: persistent setting from DB, env var overrides (crash recovery)
|
||||
let force_x11_db = graphics_db.as_ref().map(|s| s.force_x11).unwrap_or(false);
|
||||
let force_x11 = match std::env::var("QBZ_FORCE_X11").as_deref() {
|
||||
@@ -394,13 +387,8 @@ fn main() {
|
||||
}
|
||||
|
||||
// --- GDK backend selection ---
|
||||
if (force_x11 || titlebar_wants_xwayland) && is_wayland {
|
||||
if force_x11 && is_wayland {
|
||||
qbz_nix_lib::logging::log_startup("[QBZ] Forcing X11 backend on Wayland session");
|
||||
if titlebar_wants_xwayland && !force_x11 {
|
||||
qbz_nix_lib::logging::log_startup(
|
||||
"[QBZ] X11 backend forced by Plasma titlebar mode",
|
||||
);
|
||||
}
|
||||
std::env::set_var("GDK_BACKEND", "x11");
|
||||
|
||||
// GDK_SCALE is integer-only and only meaningful on X11/XWayland
|
||||
|
||||
Reference in new issue
Block a user