Files
hcie-rust-v3.05/hcie-iced-app/crates/hcie-iced-gui/src/panels/title_bar.rs
T

181 lines
7.0 KiB
Rust
Raw Normal View History

//! 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<usize>,
) -> 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()
}