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 Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 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()
}