feat(iced): add dock profile management
This commit is contained in:
@@ -181,6 +181,16 @@ pub struct HcieIcedApp {
|
||||
pub viewer_active: bool,
|
||||
/// Viewer state for directory browsing and thumbnail grid.
|
||||
pub viewer_state: crate::viewer::ViewerState,
|
||||
/// Name of the currently active dock profile (if any).
|
||||
pub active_dock_profile: Option<String>,
|
||||
/// Text input for new dock profile name.
|
||||
pub dock_profile_new_name: String,
|
||||
/// Index of the dock profile being renamed (if any).
|
||||
pub dock_profile_rename_idx: Option<usize>,
|
||||
/// Text input for renaming a dock profile.
|
||||
pub dock_profile_rename_buf: String,
|
||||
/// Whether the dock profile dialog is visible.
|
||||
pub show_dock_profile_dialog: bool,
|
||||
}
|
||||
|
||||
/// A recently opened file entry.
|
||||
@@ -509,6 +519,19 @@ pub enum Message {
|
||||
WindowClose,
|
||||
WindowId(iced::window::Id),
|
||||
|
||||
// ── Dock Profiles ───────────────────────────────────
|
||||
DockProfileSave(String),
|
||||
DockProfileLoad(String),
|
||||
DockProfileRename(String, String),
|
||||
DockProfileDelete(String),
|
||||
DockProfileRenameStart(String),
|
||||
DockProfileRenameConfirm,
|
||||
DockProfileRenameCancel,
|
||||
DockProfileNewNameChanged(String),
|
||||
DockProfileRenameChanged(String),
|
||||
DockProfileDialogShow,
|
||||
DockProfileDialogHide,
|
||||
|
||||
// ── Misc ────────────────────────────────────────────
|
||||
NoOp,
|
||||
|
||||
@@ -570,6 +593,27 @@ fn build_brush_tip(state: &ToolState) -> BrushTip {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a display label back to a PaneType for dock profile loading.
|
||||
fn pane_type_from_label(label: &str) -> Option<crate::dock::state::PaneType> {
|
||||
use crate::dock::state::PaneType;
|
||||
match label {
|
||||
"Canvas" => Some(PaneType::Canvas),
|
||||
"Layers" => Some(PaneType::Layers),
|
||||
"History" => Some(PaneType::History),
|
||||
"Brushes & Tips" => Some(PaneType::Brushes),
|
||||
"Filters" => Some(PaneType::Filters),
|
||||
"Color Palette" => Some(PaneType::ColorPicker),
|
||||
"Properties" => Some(PaneType::Properties),
|
||||
"Script" => Some(PaneType::Script),
|
||||
"AI Assistant" => Some(PaneType::AiChat),
|
||||
"Layer Details" => Some(PaneType::LayerDetails),
|
||||
"Geometry" => Some(PaneType::Geometry),
|
||||
"Tool Settings" => Some(PaneType::ToolSettings),
|
||||
"AI Script Generator" => Some(PaneType::AiScript),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to combine two bounding boxes of dirty regions.
|
||||
fn union_regions(r1: Option<[u32; 4]>, r2: [u32; 4]) -> [u32; 4] {
|
||||
match r1 {
|
||||
@@ -691,6 +735,11 @@ impl HcieIcedApp {
|
||||
ai_script_url: "http://localhost:11434".to_string(),
|
||||
viewer_active: false,
|
||||
viewer_state: crate::viewer::ViewerState::default(),
|
||||
active_dock_profile: None,
|
||||
dock_profile_new_name: String::new(),
|
||||
dock_profile_rename_idx: None,
|
||||
dock_profile_rename_buf: String::new(),
|
||||
show_dock_profile_dialog: false,
|
||||
};
|
||||
|
||||
// Apply saved settings to tool state
|
||||
@@ -2809,6 +2858,127 @@ impl HcieIcedApp {
|
||||
self.dock.toggle_maximize(pane);
|
||||
}
|
||||
|
||||
// ── Dock Profiles ─────────────────────────────
|
||||
Message::DockProfileSave(name) => {
|
||||
let name = name.trim().to_string();
|
||||
if name.is_empty() {
|
||||
return Task::none();
|
||||
}
|
||||
// Collect the list of panel types currently open in the dock
|
||||
let visible_panels: Vec<String> = self.dock.pane_grid.iter()
|
||||
.map(|(_, pane_type)| pane_type.label().to_string())
|
||||
.collect();
|
||||
let profile = crate::settings::DockProfile {
|
||||
name: name.clone(),
|
||||
visible_panels,
|
||||
left_col1_w: 0.0,
|
||||
left_col2_w: 0.0,
|
||||
right_col1_w: 0.0,
|
||||
right_col2_w: 0.0,
|
||||
last_window_width: 0.0,
|
||||
};
|
||||
self.settings.dock_profiles.retain(|p| p.name != name);
|
||||
self.settings.dock_profiles.push(profile);
|
||||
self.active_dock_profile = Some(name.clone());
|
||||
self.dock_profile_new_name.clear();
|
||||
log::info!("Dock profile saved: {}", name);
|
||||
let _ = self.settings.save();
|
||||
self.active_dialog = ActiveDialog::None;
|
||||
}
|
||||
Message::DockProfileLoad(name) => {
|
||||
if let Some(profile) = self
|
||||
.settings
|
||||
.dock_profiles
|
||||
.iter()
|
||||
.find(|p| p.name == *name)
|
||||
{
|
||||
// Reopen all panels listed in the saved profile
|
||||
let panel_names = profile.visible_panels.clone();
|
||||
self.dock = crate::dock::state::DockState::new();
|
||||
for panel_name in &panel_names {
|
||||
if let Some(pane_type) = pane_type_from_label(panel_name) {
|
||||
self.dock.reopen_pane(pane_type);
|
||||
}
|
||||
}
|
||||
self.active_dock_profile = Some(name.clone());
|
||||
log::info!("Dock profile applied: {}", name);
|
||||
}
|
||||
}
|
||||
Message::DockProfileRename(old_name, new_name) => {
|
||||
let new_name = new_name.trim().to_string();
|
||||
if new_name.is_empty() {
|
||||
return Task::none();
|
||||
}
|
||||
if let Some(profile) = self
|
||||
.settings
|
||||
.dock_profiles
|
||||
.iter_mut()
|
||||
.find(|p| p.name == old_name)
|
||||
{
|
||||
profile.name = new_name.clone();
|
||||
if self.active_dock_profile.as_deref() == Some(&old_name) {
|
||||
self.active_dock_profile = Some(new_name);
|
||||
}
|
||||
log::info!("Dock profile renamed: {} -> {}", old_name, profile.name);
|
||||
let _ = self.settings.save();
|
||||
}
|
||||
self.dock_profile_rename_idx = None;
|
||||
self.dock_profile_rename_buf.clear();
|
||||
}
|
||||
Message::DockProfileDelete(name) => {
|
||||
let before = self.settings.dock_profiles.len();
|
||||
self.settings.dock_profiles.retain(|p| p.name != *name);
|
||||
if self.settings.dock_profiles.len() < before {
|
||||
if self.active_dock_profile.as_deref() == Some(&name) {
|
||||
self.active_dock_profile = None;
|
||||
}
|
||||
log::info!("Dock profile deleted: {}", name);
|
||||
let _ = self.settings.save();
|
||||
}
|
||||
}
|
||||
Message::DockProfileRenameStart(name) => {
|
||||
if let Some(idx) = self.settings.dock_profiles.iter().position(|p| p.name == name) {
|
||||
self.dock_profile_rename_idx = Some(idx);
|
||||
self.dock_profile_rename_buf = name;
|
||||
}
|
||||
}
|
||||
Message::DockProfileRenameConfirm => {
|
||||
if let Some(idx) = self.dock_profile_rename_idx {
|
||||
if idx < self.settings.dock_profiles.len() {
|
||||
let old_name = self.settings.dock_profiles[idx].name.clone();
|
||||
let new_name = self.dock_profile_rename_buf.trim().to_string();
|
||||
if !new_name.is_empty() {
|
||||
self.settings.dock_profiles[idx].name = new_name.clone();
|
||||
if self.active_dock_profile.as_deref() == Some(&old_name) {
|
||||
self.active_dock_profile = Some(new_name);
|
||||
}
|
||||
log::info!("Dock profile renamed: {} -> {}", old_name, self.settings.dock_profiles[idx].name);
|
||||
let _ = self.settings.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
self.dock_profile_rename_idx = None;
|
||||
self.dock_profile_rename_buf.clear();
|
||||
}
|
||||
Message::DockProfileRenameCancel => {
|
||||
self.dock_profile_rename_idx = None;
|
||||
self.dock_profile_rename_buf.clear();
|
||||
}
|
||||
Message::DockProfileNewNameChanged(name) => {
|
||||
self.dock_profile_new_name = name;
|
||||
}
|
||||
Message::DockProfileRenameChanged(name) => {
|
||||
self.dock_profile_rename_buf = name;
|
||||
}
|
||||
Message::DockProfileDialogShow => {
|
||||
self.show_dock_profile_dialog = true;
|
||||
}
|
||||
Message::DockProfileDialogHide => {
|
||||
self.show_dock_profile_dialog = false;
|
||||
self.dock_profile_rename_idx = None;
|
||||
self.dock_profile_rename_buf.clear();
|
||||
}
|
||||
|
||||
// ── File I/O ─────────────────────────────────
|
||||
Message::NewDocument(w, h) => {
|
||||
let mut engine = Engine::new(w, h);
|
||||
@@ -3511,17 +3681,25 @@ impl HcieIcedApp {
|
||||
}
|
||||
};
|
||||
|
||||
// Stack main content with overlays (dialog + menu dropdown).
|
||||
// Stack main content with overlays (dialog + menu dropdown + dock profile dialog).
|
||||
// `content` is already wrapped in a styled container with the app
|
||||
// background, so no extra wrapper is needed.
|
||||
let has_dialog = self.active_dialog != ActiveDialog::None;
|
||||
let has_menu = self.active_menu.is_some();
|
||||
let has_dock_profile_dialog = self.show_dock_profile_dialog;
|
||||
|
||||
if has_dialog || has_menu {
|
||||
if has_dialog || has_menu || has_dock_profile_dialog {
|
||||
let mut stack = iced::widget::Stack::new().push(content);
|
||||
if has_dialog {
|
||||
stack = stack.push(dialog_overlay);
|
||||
}
|
||||
if has_dock_profile_dialog {
|
||||
stack = stack.push(dialogs::dock_profile::view(
|
||||
&self.settings.dock_profiles,
|
||||
self.active_dock_profile.as_deref(),
|
||||
&self.dock_profile_new_name,
|
||||
));
|
||||
}
|
||||
if let Some(menu_overlay) = panels::menus::dropdown_overlay(self.active_menu, &self.recent_files, self.theme_state.colors()) {
|
||||
stack = stack.push(menu_overlay);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user