269 lines
7.9 KiB
Rust
269 lines
7.9 KiB
Rust
use iced::widget::text::Shaping;
|
|
use iced::widget::{Column, Space, button, container, row, svg, text};
|
|
use iced::{Background, Border, Color, Element, Length, Theme};
|
|
|
|
use bds_core::i18n::UiLocale;
|
|
use bds_core::model::Project;
|
|
|
|
use crate::app::Message;
|
|
use crate::i18n::t;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Styles
|
|
// ---------------------------------------------------------------------------
|
|
|
|
fn dropdown_bg(_theme: &Theme) -> container::Style {
|
|
container::Style {
|
|
background: Some(Background::Color(Color::from_rgb(0.18, 0.18, 0.22))),
|
|
border: Border {
|
|
color: Color::from_rgb(0.30, 0.30, 0.35),
|
|
width: 1.0,
|
|
radius: 10.0.into(),
|
|
},
|
|
..container::Style::default()
|
|
}
|
|
}
|
|
|
|
fn project_item(_theme: &Theme, status: button::Status) -> button::Style {
|
|
let bg = match status {
|
|
button::Status::Hovered => Color::from_rgb(0.25, 0.25, 0.30),
|
|
_ => Color::from_rgb(0.18, 0.18, 0.22),
|
|
};
|
|
button::Style {
|
|
background: Some(Background::Color(bg)),
|
|
text_color: Color::WHITE,
|
|
border: Border {
|
|
color: Color::TRANSPARENT,
|
|
width: 0.0,
|
|
radius: 6.0.into(),
|
|
},
|
|
..button::Style::default()
|
|
}
|
|
}
|
|
|
|
fn project_item_active(_theme: &Theme, status: button::Status) -> button::Style {
|
|
let bg = match status {
|
|
button::Status::Hovered => Color::from_rgb(0.25, 0.32, 0.45),
|
|
_ => Color::from_rgb(0.20, 0.28, 0.40),
|
|
};
|
|
button::Style {
|
|
background: Some(Background::Color(bg)),
|
|
text_color: Color::WHITE,
|
|
border: Border {
|
|
color: Color::TRANSPARENT,
|
|
width: 0.0,
|
|
radius: 6.0.into(),
|
|
},
|
|
..button::Style::default()
|
|
}
|
|
}
|
|
|
|
fn new_project_btn(_theme: &Theme, status: button::Status) -> button::Style {
|
|
let bg = match status {
|
|
button::Status::Hovered => Color::from_rgb(0.25, 0.25, 0.30),
|
|
_ => Color::TRANSPARENT,
|
|
};
|
|
button::Style {
|
|
background: Some(Background::Color(bg)),
|
|
text_color: Color::from_rgb(0.55, 0.75, 0.95),
|
|
border: Border {
|
|
color: Color::TRANSPARENT,
|
|
width: 0.0,
|
|
radius: 6.0.into(),
|
|
},
|
|
..button::Style::default()
|
|
}
|
|
}
|
|
|
|
fn header_style(_theme: &Theme) -> container::Style {
|
|
container::Style {
|
|
border: Border {
|
|
color: Color::from_rgb(0.30, 0.30, 0.35),
|
|
width: 0.0,
|
|
radius: 0.0.into(),
|
|
},
|
|
..container::Style::default()
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Folder icon SVG (16x16)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const FOLDER_ICON: &[u8] = include_bytes!("../../assets/icons/folder.svg");
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// View: renders the dropdown content (project list + new project)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
pub fn view(
|
|
projects: &[Project],
|
|
active_project_id: Option<&str>,
|
|
locale: UiLocale,
|
|
) -> Element<'static, Message> {
|
|
let header = container(
|
|
text(t(locale, "projectSelector.projectsHeader"))
|
|
.size(11)
|
|
.shaping(Shaping::Advanced)
|
|
.color(Color::from_rgb(0.55, 0.55, 0.60)),
|
|
)
|
|
.padding([4, 8])
|
|
.style(header_style);
|
|
|
|
let mut items: Vec<Element<'static, Message>> = Vec::new();
|
|
items.push(header.into());
|
|
|
|
for project in projects {
|
|
let is_active = active_project_id == Some(project.id.as_str());
|
|
let name = project.name.clone();
|
|
let id = project.id.clone();
|
|
|
|
let label = if is_active {
|
|
row![
|
|
text("\u{2713}")
|
|
.size(12)
|
|
.shaping(Shaping::Advanced)
|
|
.color(Color::from_rgb(0.40, 0.80, 0.40)),
|
|
text(name)
|
|
.size(12)
|
|
.shaping(Shaping::Advanced)
|
|
.color(Color::WHITE),
|
|
]
|
|
.spacing(6)
|
|
} else {
|
|
row![
|
|
Space::with_width(Length::Fixed(14.0)),
|
|
text(name)
|
|
.size(12)
|
|
.shaping(Shaping::Advanced)
|
|
.color(Color::from_rgb(0.80, 0.80, 0.85)),
|
|
]
|
|
.spacing(6)
|
|
};
|
|
|
|
let style_fn = if is_active {
|
|
project_item_active
|
|
} else {
|
|
project_item
|
|
};
|
|
|
|
items.push(
|
|
button(label)
|
|
.on_press(Message::SwitchProject(id))
|
|
.padding([4, 8])
|
|
.width(Length::Fill)
|
|
.style(style_fn)
|
|
.into(),
|
|
);
|
|
}
|
|
|
|
// Separator
|
|
items.push(
|
|
container(Space::new(Length::Fill, Length::Fixed(1.0)))
|
|
.style(|_theme: &Theme| container::Style {
|
|
background: Some(Background::Color(Color::from_rgb(0.30, 0.30, 0.35))),
|
|
..container::Style::default()
|
|
})
|
|
.width(Length::Fill)
|
|
.into(),
|
|
);
|
|
|
|
// Open and create project actions
|
|
items.push(
|
|
button(
|
|
row![
|
|
text("↗")
|
|
.size(14)
|
|
.shaping(Shaping::Advanced)
|
|
.color(Color::from_rgb(0.55, 0.75, 0.95)),
|
|
text(t(locale, "projectSelector.openProject"))
|
|
.size(12)
|
|
.shaping(Shaping::Advanced),
|
|
]
|
|
.spacing(6),
|
|
)
|
|
.on_press(Message::RequestOpenProject)
|
|
.padding([4, 8])
|
|
.width(Length::Fill)
|
|
.style(new_project_btn)
|
|
.into(),
|
|
);
|
|
|
|
items.push(
|
|
button(
|
|
row![
|
|
text("+")
|
|
.size(14)
|
|
.shaping(Shaping::Advanced)
|
|
.color(Color::from_rgb(0.55, 0.75, 0.95)),
|
|
text(t(locale, "projectSelector.newProject"))
|
|
.size(12)
|
|
.shaping(Shaping::Advanced),
|
|
]
|
|
.spacing(6),
|
|
)
|
|
.on_press(Message::RequestCreateProject)
|
|
.padding([4, 8])
|
|
.width(Length::Fill)
|
|
.style(new_project_btn)
|
|
.into(),
|
|
);
|
|
|
|
container(
|
|
Column::with_children(items)
|
|
.spacing(2)
|
|
.padding(4)
|
|
.width(Length::Fixed(200.0)),
|
|
)
|
|
.style(dropdown_bg)
|
|
.into()
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Trigger button for status bar (folder icon + project name + chevron)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
pub fn trigger_style(_theme: &Theme, status: button::Status) -> button::Style {
|
|
let bg = match status {
|
|
button::Status::Hovered => Color::from_rgb(0.22, 0.22, 0.27),
|
|
_ => Color::TRANSPARENT,
|
|
};
|
|
button::Style {
|
|
background: Some(Background::Color(bg)),
|
|
text_color: Color::WHITE,
|
|
border: Border {
|
|
color: Color::TRANSPARENT,
|
|
width: 0.0,
|
|
radius: 6.0.into(),
|
|
},
|
|
..button::Style::default()
|
|
}
|
|
}
|
|
|
|
pub fn trigger_button(project_name: &str) -> Element<'static, Message> {
|
|
let folder = svg::Handle::from_memory(FOLDER_ICON);
|
|
let folder_icon = svg(folder)
|
|
.width(Length::Fixed(14.0))
|
|
.height(Length::Fixed(14.0));
|
|
|
|
let name = text(project_name.to_string())
|
|
.size(12)
|
|
.shaping(Shaping::Advanced)
|
|
.color(Color::from_rgb(0.80, 0.80, 0.85));
|
|
|
|
let chevron = text("\u{25BE}")
|
|
.size(10)
|
|
.shaping(Shaping::Advanced)
|
|
.color(Color::from_rgb(0.55, 0.55, 0.60));
|
|
|
|
button(
|
|
row![folder_icon, name, chevron]
|
|
.spacing(4)
|
|
.align_y(iced::Alignment::Center),
|
|
)
|
|
.on_press(Message::ToggleProjectDropdown)
|
|
.padding([2, 6])
|
|
.style(trigger_style)
|
|
.into()
|
|
}
|