use crate::atoms::icons;
use egui::{Color32, RichText};
use egui_cha::router::{Router, RouterMsg};
use egui_cha::ViewCtx;
struct NavbarColors {
bg: Color32,
text_active: Color32,
text_inactive: Color32,
button_active_bg: Color32,
}
impl NavbarColors {
fn from_dark_mode(is_dark: bool) -> Self {
if is_dark {
Self {
bg: Color32::from_rgb(17, 24, 39), text_active: Color32::from_rgb(249, 250, 251), text_inactive: Color32::from_rgb(156, 163, 175), button_active_bg: Color32::from_rgb(55, 65, 81), }
} else {
Self {
bg: Color32::from_rgb(31, 41, 55), text_active: Color32::WHITE,
text_inactive: Color32::from_rgb(156, 163, 175),
button_active_bg: Color32::from_rgb(55, 65, 81),
}
}
}
}
pub struct Navbar<'a> {
title: Option<&'a str>,
show_back: bool,
}
impl<'a> Navbar<'a> {
pub fn new() -> Self {
Self {
title: None,
show_back: false,
}
}
pub fn title(mut self, title: &'a str) -> Self {
self.title = Some(title);
self
}
pub fn with_back(mut self) -> Self {
self.show_back = true;
self
}
pub fn show<P, Msg>(
self,
ctx: &mut ViewCtx<'_, Msg>,
router: &Router<P>,
items: &[(&str, P)],
_to_msg: impl Fn(RouterMsg<P>) -> Msg + Clone,
) where
P: Clone + PartialEq,
{
let is_dark = ctx.ui.ctx().style().visuals.dark_mode;
let colors = NavbarColors::from_dark_mode(is_dark);
egui::Frame::new()
.fill(colors.bg)
.inner_margin(egui::Margin::symmetric(16, 8))
.show(ctx.ui, |ui| {
ui.horizontal(|ui| {
if self.show_back {
let enabled = router.can_back();
let back_icon = RichText::new(icons::ARROW_LEFT)
.family(egui::FontFamily::Name("icons".into()));
if ui
.add_enabled(enabled, egui::Button::new(back_icon))
.clicked()
{
}
}
if let Some(title) = self.title {
ui.label(RichText::new(title).strong().color(colors.text_active));
ui.add_space(16.0);
}
for (label, page) in items {
let is_active = router.is_at(page);
let text = RichText::new(*label).color(if is_active {
colors.text_active
} else {
colors.text_inactive
});
let button = if is_active {
egui::Button::new(text).fill(colors.button_active_bg)
} else {
egui::Button::new(text).fill(Color32::TRANSPARENT)
};
if ui.add(button).clicked() && !is_active {
}
}
});
});
}
}
impl<'a> Default for Navbar<'a> {
fn default() -> Self {
Self::new()
}
}
fn nav_active_color(is_dark: bool) -> Color32 {
if is_dark {
Color32::from_rgb(96, 165, 250) } else {
Color32::from_rgb(59, 130, 246) }
}
fn nav_active_text_color(is_dark: bool) -> Color32 {
if is_dark {
Color32::from_rgb(17, 24, 39) } else {
Color32::WHITE
}
}
pub fn navbar<P, Msg>(
ctx: &mut ViewCtx<'_, Msg>,
router: &Router<P>,
items: &[(&str, P)],
to_msg: impl Fn(RouterMsg<P>) -> Msg,
) where
P: Clone + PartialEq,
{
let is_dark = ctx.ui.ctx().style().visuals.dark_mode;
let active_bg = nav_active_color(is_dark);
let active_text = nav_active_text_color(is_dark);
let mut nav_action: Option<RouterMsg<P>> = None;
ctx.ui.horizontal(|ui| {
if router.can_back() {
let back_icon =
RichText::new(icons::ARROW_LEFT).family(egui::FontFamily::Name("icons".into()));
if ui.button(back_icon).clicked() {
nav_action = Some(RouterMsg::Back);
}
}
ui.separator();
for (label, page) in items {
let is_active = router.is_at(page);
let response = if is_active {
ui.add(egui::Button::new(RichText::new(*label).color(active_text)).fill(active_bg))
} else {
ui.button(*label)
};
if response.clicked() && !is_active {
nav_action = Some(RouterMsg::Navigate(page.clone()));
}
}
});
if let Some(action) = nav_action {
ctx.emit(to_msg(action));
}
}
pub fn sidebar<P, Msg>(
ctx: &mut ViewCtx<'_, Msg>,
router: &Router<P>,
items: &[(&str, P)],
to_msg: impl Fn(RouterMsg<P>) -> Msg,
) where
P: Clone + PartialEq,
{
let is_dark = ctx.ui.ctx().style().visuals.dark_mode;
let active_bg = nav_active_color(is_dark);
let active_text = nav_active_text_color(is_dark);
let mut nav_action: Option<RouterMsg<P>> = None;
ctx.ui.vertical(|ui| {
for (label, page) in items {
let is_active = router.is_at(page);
let response = if is_active {
ui.add(
egui::Button::new(RichText::new(*label).strong().color(active_text))
.fill(active_bg)
.min_size(egui::vec2(ui.available_width(), 0.0)),
)
} else {
ui.add(
egui::Button::new(RichText::new(*label))
.fill(Color32::TRANSPARENT)
.min_size(egui::vec2(ui.available_width(), 0.0)),
)
};
if response.clicked() && !is_active {
nav_action = Some(RouterMsg::Navigate(page.clone()));
}
}
});
if let Some(action) = nav_action {
ctx.emit(to_msg(action));
}
}