//! Title bar — Photoshop-style unified bar with menus, doc info, and window controls. //! //! Combines the menu bar and title bar into a single 28px bar: //! [HCIE logo] [menu items...] [doc info] [min/max/close] //! The entire bar is draggable for window movement. use crate::app::Message; use crate::theme::ThemeColors; use iced::widget::{button, container, mouse_area, row, text}; use iced::{Element, Length}; /// All menu definitions for the menu bar. const MENU_LABELS: &[&str] = &["File", "Edit", "Tools", "Image", "Layer", "Filter", "Select", "View", "Window", "Help"]; /// Build the unified title/menu bar element. /// /// Layout: [HCIE] [File Edit Tools ... Help] [doc info] [─ □ ×] /// The left portion (before window controls) is draggable. pub fn view<'a>( doc_name: &'a str, zoom: f32, canvas_w: u32, canvas_h: u32, colors: ThemeColors, active_menu: Option, ) -> Element<'a, Message> { // ── App name (fixed width) ── let app_name = text("HCIE") .size(13) .font(iced::Font::MONOSPACE) .style(move |_theme: &iced::Theme| iced::widget::text::Style { color: Some(colors.accent), }); // ── Menu items with hover states ── let mut menu_items = row![].spacing(0); for (idx, &label) in MENU_LABELS.iter().enumerate() { let is_open = active_menu == Some(idx); let c = colors; let btn = button(text(label).size(11)) .on_press(Message::MenuOpen(idx)) .padding([4, 8]) .style(move |_theme: &iced::Theme, status: iced::widget::button::Status| { if is_open { iced::widget::button::Style { background: Some(iced::Background::Color(c.bg_hover)), text_color: c.text_primary, border: iced::Border::default().color(c.border_high).width(1), ..Default::default() } } else { match status { iced::widget::button::Status::Hovered => iced::widget::button::Style { background: Some(iced::Background::Color(c.bg_hover)), text_color: c.text_primary, border: iced::Border::default(), ..Default::default() }, _ => iced::widget::button::Style { background: Some(iced::Background::Color(iced::Color::from_rgba(0.0, 0.0, 0.0, 0.0))), text_color: c.text_primary, border: iced::Border::default(), ..Default::default() }, } } }); menu_items = menu_items.push(btn); } // ── Document info ── let doc_info = text(format!("{} — {}×{} @ {:.0}%", doc_name, canvas_w, canvas_h, zoom * 100.0)) .size(10) .style(move |_theme: &iced::Theme| iced::widget::text::Style { color: Some(colors.text_secondary), }); // ── Window controls (right side) with hover states ── let c = colors; let minimize_btn = button(text("─").size(10)) .on_press(Message::WindowMinimize) .padding([4, 8]) .style(move |_theme: &iced::Theme, status: iced::widget::button::Status| { match status { iced::widget::button::Status::Hovered => iced::widget::button::Style { background: Some(iced::Background::Color(c.bg_hover)), text_color: c.text_primary, border: iced::Border::default(), ..Default::default() }, _ => iced::widget::button::Style { background: Some(iced::Background::Color(iced::Color::from_rgba(0.0, 0.0, 0.0, 0.0))), text_color: c.text_secondary, border: iced::Border::default(), ..Default::default() }, } }); let maximize_btn = button(text("□").size(10)) .on_press(Message::WindowMaximize) .padding([4, 8]) .style(move |_theme: &iced::Theme, status: iced::widget::button::Status| { match status { iced::widget::button::Status::Hovered => iced::widget::button::Style { background: Some(iced::Background::Color(c.bg_hover)), text_color: c.text_primary, border: iced::Border::default(), ..Default::default() }, _ => iced::widget::button::Style { background: Some(iced::Background::Color(iced::Color::from_rgba(0.0, 0.0, 0.0, 0.0))), text_color: c.text_secondary, border: iced::Border::default(), ..Default::default() }, } }); let close_btn = button(text("×").size(11)) .on_press(Message::WindowClose) .padding([4, 10]) .style(move |_theme: &iced::Theme, status: iced::widget::button::Status| { match status { iced::widget::button::Status::Hovered => iced::widget::button::Style { background: Some(iced::Background::Color(c.danger)), text_color: iced::Color::WHITE, border: iced::Border::default(), ..Default::default() }, _ => iced::widget::button::Style { background: Some(iced::Background::Color(iced::Color::from_rgba(0.0, 0.0, 0.0, 0.0))), text_color: c.text_secondary, border: iced::Border::default(), ..Default::default() }, } }); let window_controls = row![minimize_btn, maximize_btn, close_btn].spacing(0); // ── Left side (draggable): app name + menus + doc info ── let left_side = row![ app_name, text(" ").size(11), menu_items, iced::widget::Space::with_width(Length::Fill), doc_info, ] .spacing(0) .align_y(iced::Alignment::Center); let draggable_left = mouse_area( container(left_side) .width(Length::Fill) .height(28) .align_y(iced::Alignment::Center) .padding([0, 8]) ) .on_press(Message::WindowDrag) .interaction(iced::mouse::Interaction::Grab); // ── Full bar ── let bar = row![ draggable_left, window_controls, ] .align_y(iced::Alignment::Center); container(bar) .width(Length::Fill) .height(28) .style(move |_theme| iced::widget::container::Style { background: Some(iced::Background::Color(colors.bg_app)), border: iced::Border::default().color(colors.border_low).width(1), ..Default::default() }) .into() }