Implement Photopea theme and tool settings panel
- Added Photopea theme implementation report and design specifications. - Created tool options and settings panels to match Photopea's layout. - Implemented settings persistence to save and load user preferences. - Updated toolbar to include SVG icons and tool options in a single row. - Enhanced tool settings with specific parameters for each tool type. - Added functionality for resetting tool settings to defaults.
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
//! Settings persistence — saves/loads all app settings to JSON.
|
||||
//!
|
||||
//! **Purpose:** Persists tool settings, panel layout, and preferences between sessions.
|
||||
//! Settings are saved to `~/.hcie/settings.json` and loaded on startup.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Main settings structure saved to disk.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AppSettings {
|
||||
/// Tool-specific settings.
|
||||
pub tool_settings: ToolSettings,
|
||||
/// Panel layout positions.
|
||||
pub panel_layout: PanelLayout,
|
||||
/// Window settings.
|
||||
pub window: WindowSettings,
|
||||
}
|
||||
|
||||
/// Tool-specific settings that persist between sessions.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ToolSettings {
|
||||
pub brush_size: f32,
|
||||
pub brush_opacity: f32,
|
||||
pub brush_hardness: f32,
|
||||
pub brush_flow: f32,
|
||||
pub brush_spacing: f32,
|
||||
pub eraser_size: f32,
|
||||
pub eraser_opacity: f32,
|
||||
pub pen_size: f32,
|
||||
pub pen_opacity: f32,
|
||||
pub spray_radius: f32,
|
||||
pub spray_opacity: f32,
|
||||
pub gradient_opacity: f32,
|
||||
pub flood_fill_tolerance: f32,
|
||||
pub magic_wand_tolerance: f32,
|
||||
pub select_feather: f32,
|
||||
pub text_size: f32,
|
||||
pub vector_stroke_width: f32,
|
||||
pub vector_opacity: f32,
|
||||
pub last_active_tool: String,
|
||||
}
|
||||
|
||||
/// Panel layout positions and visibility.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PanelLayout {
|
||||
/// Left sidebar width in pixels.
|
||||
pub sidebar_width: f32,
|
||||
/// Right tool settings panel width in pixels.
|
||||
pub tool_settings_width: f32,
|
||||
/// Which panels are visible.
|
||||
pub visible_panels: Vec<String>,
|
||||
/// Panel order in the dock.
|
||||
pub panel_order: Vec<String>,
|
||||
}
|
||||
|
||||
/// Window settings.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WindowSettings {
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
pub maximized: bool,
|
||||
}
|
||||
|
||||
impl Default for AppSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
tool_settings: ToolSettings::default(),
|
||||
panel_layout: PanelLayout::default(),
|
||||
window: WindowSettings::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ToolSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
brush_size: 20.0,
|
||||
brush_opacity: 1.0,
|
||||
brush_hardness: 0.8,
|
||||
brush_flow: 1.0,
|
||||
brush_spacing: 1.0,
|
||||
eraser_size: 20.0,
|
||||
eraser_opacity: 1.0,
|
||||
pen_size: 2.0,
|
||||
pen_opacity: 1.0,
|
||||
spray_radius: 40.0,
|
||||
spray_opacity: 0.5,
|
||||
gradient_opacity: 1.0,
|
||||
flood_fill_tolerance: 32.0,
|
||||
magic_wand_tolerance: 16.0,
|
||||
select_feather: 0.0,
|
||||
text_size: 24.0,
|
||||
vector_stroke_width: 1.0,
|
||||
vector_opacity: 1.0,
|
||||
last_active_tool: "Brush".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PanelLayout {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sidebar_width: 36.0,
|
||||
tool_settings_width: 200.0,
|
||||
visible_panels: vec![
|
||||
"Brushes & Tips".to_string(),
|
||||
"Color Palette".to_string(),
|
||||
"Layers".to_string(),
|
||||
"History".to_string(),
|
||||
"Filters".to_string(),
|
||||
],
|
||||
panel_order: vec![
|
||||
"Brushes & Tips".to_string(),
|
||||
"Color Palette".to_string(),
|
||||
"Layers".to_string(),
|
||||
"History".to_string(),
|
||||
"Filters".to_string(),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WindowSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
width: 1280.0,
|
||||
height: 800.0,
|
||||
maximized: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppSettings {
|
||||
/// Get the settings file path (~/.hcie/settings.json).
|
||||
pub fn file_path() -> PathBuf {
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string());
|
||||
PathBuf::from(home).join(".hcie").join("settings.json")
|
||||
}
|
||||
|
||||
/// Load settings from disk, or return defaults if file doesn't exist.
|
||||
pub fn load() -> Self {
|
||||
let path = Self::file_path();
|
||||
if path.exists() {
|
||||
match std::fs::read_to_string(&path) {
|
||||
Ok(content) => {
|
||||
match serde_json::from_str(&content) {
|
||||
Ok(settings) => settings,
|
||||
Err(e) => {
|
||||
log::warn!("Failed to parse settings: {}, using defaults", e);
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Failed to read settings: {}, using defaults", e);
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Save settings to disk.
|
||||
pub fn save(&self) -> Result<(), String> {
|
||||
let path = Self::file_path();
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
if let Some(parent) = path.parent() {
|
||||
std::fs::create_dir_all(parent)
|
||||
.map_err(|e| format!("Failed to create settings directory: {}", e))?;
|
||||
}
|
||||
|
||||
let content = serde_json::to_string_pretty(self)
|
||||
.map_err(|e| format!("Failed to serialize settings: {}", e))?;
|
||||
|
||||
std::fs::write(&path, content)
|
||||
.map_err(|e| format!("Failed to write settings: {}", e))?;
|
||||
|
||||
log::info!("Settings saved to {}", path.display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reset all tool settings to defaults.
|
||||
pub fn reset_tool_defaults(&mut self) {
|
||||
self.tool_settings = ToolSettings::default();
|
||||
}
|
||||
|
||||
/// Update tool settings from current ToolState.
|
||||
pub fn update_from_tool_state(&mut self, tool_state: &crate::app::ToolState) {
|
||||
self.tool_settings.brush_size = tool_state.brush_size;
|
||||
self.tool_settings.brush_opacity = tool_state.brush_opacity;
|
||||
self.tool_settings.brush_hardness = tool_state.brush_hardness;
|
||||
self.tool_settings.last_active_tool = format!("{:?}", tool_state.active_tool);
|
||||
}
|
||||
|
||||
/// Apply saved settings to ToolState.
|
||||
pub fn apply_to_tool_state(&self, tool_state: &mut crate::app::ToolState) {
|
||||
tool_state.brush_size = self.tool_settings.brush_size;
|
||||
tool_state.brush_opacity = self.tool_settings.brush_opacity;
|
||||
tool_state.brush_hardness = self.tool_settings.brush_hardness;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user