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:
vicrodh committed 2026-05-09 17:48:37 -06:00
1 parent f65248a917
commit e3bdbc9ab4
16 files changed
+287 -782

No files matched your search

+3 -25
View File
@@ -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]
+2 -166
View File
@@ -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
View File
@@ -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
View File
@@ -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