Werkzeugleiste - Schmale senkrechte Leiste statt ~30 Elemente in einer Zeile; seitlich oben angedockt (links, im ☰-Menü auf rechts umschaltbar, gespeichert in ~/.config/ink-notes/ui.json) - Symbol-Knöpfe 40 px, auf niedrigen Fenstern schrittweise bis 28 px; Größe richtet sich nach dem höchsten Fall, damit Knöpfe beim Werkzeugwechsel nicht springen (fester Platz für die Farbpalette) - Selten genutztes (PDF, Hintergrund, Zoom, Finger-Zeichnen, Leistenseite, Updates, Speicherstatus) im ☰-Menü; Popover öffnen zur Blattseite hin - Blatt wird im freien Bereich neben der Leiste zentriert - Symbole ohne Glyphe in den egui-Schriften (🖍 ⌫ ⬚ ◄ ► ● ✕ ▾) ersetzt bzw. selbst gezeichnet; Hover-Farbe korrigiert (falsch vormultipliziert) Eingabe - UI-Trefferprüfung in logischen Punkten (vorher physische Pixel gegen egui-Koordinaten: bei Skalierung ≠ 1 trafen Stift und Touch die Leiste nicht) und über egui::Context::layer_id_at für alle egui-Bereiche - Stift-Ereignisse über egui_input_mut() statt ctx.input_mut() (wurden vom nächsten Frame verworfen); Stiftbewegungen für Hover/Ziehen weitergereicht, Stift bleibt vom Aufsetzen bis Abheben bei der UI - Berührungen, die auf der UI beginnen, bleiben bis zum Abheben bei egui - egui-Neuzeichenwünsche (repaint_delay) werden beachtet Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
85 lines
2.8 KiB
Rust
85 lines
2.8 KiB
Rust
//! Persistente UI-Einstellungen (`$XDG_CONFIG_HOME/ink-notes/ui.json`).
|
|
//!
|
|
//! Fehlende oder unlesbare Dateien führen zu Standardwerten, damit die App immer startet.
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::storage::{self, StorageError};
|
|
|
|
/// Fensterseite, an der die Werkzeugleiste angedockt ist.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
|
pub enum SidebarSide {
|
|
/// Links (Standard: die Schreibhand von Rechtshändern verdeckt die Leiste nicht)
|
|
#[default]
|
|
Left,
|
|
/// Rechts (für Linkshänder)
|
|
Right,
|
|
}
|
|
|
|
/// Benutzereinstellungen der Oberfläche.
|
|
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct UiSettings {
|
|
pub sidebar_side: SidebarSide,
|
|
}
|
|
|
|
impl UiSettings {
|
|
/// Standardpfad gemäß XDG-Spezifikation.
|
|
pub fn default_path() -> Option<PathBuf> {
|
|
let base = match std::env::var("XDG_CONFIG_HOME") {
|
|
Ok(dir) if !dir.trim().is_empty() => PathBuf::from(dir),
|
|
_ => PathBuf::from(std::env::var("HOME").ok()?).join(".config"),
|
|
};
|
|
Some(base.join("ink-notes").join("ui.json"))
|
|
}
|
|
|
|
/// Lädt die Einstellungen; bei fehlender oder beschädigter Datei gelten Standardwerte.
|
|
pub fn load_from(path: &Path) -> Self {
|
|
match std::fs::read_to_string(path) {
|
|
Ok(content) => serde_json::from_str(&content).unwrap_or_else(|e| {
|
|
eprintln!(
|
|
"[Einstellungen] '{}' ist ungültig ({e}), verwende Standardwerte.",
|
|
path.display()
|
|
);
|
|
Self::default()
|
|
}),
|
|
Err(_) => Self::default(),
|
|
}
|
|
}
|
|
|
|
/// Speichert die Einstellungen atomar.
|
|
pub fn save_to(&self, path: &Path) -> Result<(), StorageError> {
|
|
storage::atomic_write_json(path, self)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_settings_round_trip_and_fallbacks() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let path = dir.path().join("ink-notes").join("ui.json");
|
|
|
|
// Fehlende Datei -> Standard (links)
|
|
assert_eq!(UiSettings::load_from(&path).sidebar_side, SidebarSide::Left);
|
|
|
|
let settings = UiSettings {
|
|
sidebar_side: SidebarSide::Right,
|
|
};
|
|
settings.save_to(&path).unwrap();
|
|
assert_eq!(UiSettings::load_from(&path), settings);
|
|
|
|
// Beschädigte Datei -> Standard statt Absturz
|
|
std::fs::write(&path, b"{ kaputt").unwrap();
|
|
assert_eq!(UiSettings::load_from(&path), UiSettings::default());
|
|
|
|
// Unbekannte / fehlende Felder werden toleriert
|
|
std::fs::write(&path, br#"{"zukunft": 1}"#).unwrap();
|
|
assert_eq!(UiSettings::load_from(&path), UiSettings::default());
|
|
}
|
|
}
|