use crate::get_global_color;
use egui::{
Align, Color32, CornerRadius, Layout, Response, Shadow, Stroke, Ui, Vec2, Widget,
};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ToolbarElevation {
Level0,
Level1,
Level2,
Level3,
}
#[must_use = "You should put this widget in a ui with `ui.add(widget);`"]
pub struct MaterialToolbar<'a> {
items: Vec<ToolbarItem<'a>>,
top: bool,
tabbar: bool,
tabbar_icons: bool,
tabbar_labels: bool,
outline: bool,
bg_color: Option<Color32>,
elevation: Option<ToolbarElevation>,
min_height: f32,
item_spacing: f32,
padding: Vec2,
}
enum ToolbarItem<'a> {
Widget(Box<dyn FnOnce(&mut Ui) -> Response + 'a>),
Spacer,
}
impl<'a> Default for MaterialToolbar<'a> {
fn default() -> Self {
Self {
items: Vec::new(),
top: false,
tabbar: false,
tabbar_icons: false,
tabbar_labels: false,
outline: true,
bg_color: None,
elevation: None, min_height: 56.0, item_spacing: 8.0,
padding: Vec2::new(16.0, 8.0),
}
}
}
impl<'a> MaterialToolbar<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn item<W>(mut self, widget: W) -> Self
where
W: Widget + 'a,
{
self.items.push(ToolbarItem::Widget(Box::new(move |ui| {
ui.add(widget)
})));
self
}
pub fn item_fn<F>(mut self, f: F) -> Self
where
F: FnOnce(&mut Ui) -> Response + 'a,
{
self.items.push(ToolbarItem::Widget(Box::new(f)));
self
}
pub fn spacer(mut self) -> Self {
self.items.push(ToolbarItem::Spacer);
self
}
pub fn top(mut self, top: bool) -> Self {
self.top = top;
self
}
pub fn tabbar(mut self, tabbar: bool) -> Self {
self.tabbar = tabbar;
self
}
pub fn tabbar_icons(mut self, show: bool) -> Self {
self.tabbar_icons = show;
self
}
pub fn tabbar_labels(mut self, show: bool) -> Self {
self.tabbar_labels = show;
self
}
pub fn outline(mut self, outline: bool) -> Self {
self.outline = outline;
self
}
pub fn bg_color(mut self, color: Color32) -> Self {
self.bg_color = Some(color);
self
}
pub fn elevation(mut self, elevation: ToolbarElevation) -> Self {
self.elevation = Some(elevation);
self
}
pub fn min_height(mut self, height: f32) -> Self {
self.min_height = height;
self
}
pub fn item_spacing(mut self, spacing: f32) -> Self {
self.item_spacing = spacing;
self
}
pub fn padding(mut self, padding: Vec2) -> Self {
self.padding = padding;
self
}
}
impl<'a> Widget for MaterialToolbar<'a> {
fn ui(self, ui: &mut Ui) -> Response {
let surface = get_global_color("surface");
let surface_container_low = get_global_color("surfaceContainerLow");
let surface_container = get_global_color("surfaceContainer");
let surface_container_high = get_global_color("surfaceContainerHigh");
let outline_variant = get_global_color("outlineVariant");
let _on_surface = get_global_color("onSurface");
let _on_surface_variant = get_global_color("onSurfaceVariant");
let bg_color = self.bg_color.unwrap_or_else(|| {
if let Some(elev) = self.elevation {
match elev {
ToolbarElevation::Level0 => surface,
ToolbarElevation::Level1 => surface_container_low,
ToolbarElevation::Level2 => surface_container,
ToolbarElevation::Level3 => surface_container_high,
}
} else if self.top {
surface
} else {
surface_container
}
});
let available_width = ui.available_width();
let (rect, response) = ui.allocate_exact_size(
Vec2::new(available_width, self.min_height),
egui::Sense::hover(),
);
if ui.is_rect_visible(rect) {
if self.top {
let shadow = Shadow {
offset: [0, 1],
blur: 3,
spread: 0,
color: Color32::from_black_alpha(12), };
let shadow_offset = Vec2::new(shadow.offset[0] as f32, shadow.offset[1] as f32);
let shadow_rect = rect.translate(shadow_offset);
for i in 0..2 {
let blur_offset = i as f32 * 1.0;
let alpha = (12 / (i + 1)) as u8;
ui.painter().rect_filled(
shadow_rect.expand(blur_offset),
CornerRadius::ZERO,
Color32::from_black_alpha(alpha),
);
}
}
ui.painter().rect_filled(
rect,
CornerRadius::ZERO,
bg_color,
);
if self.outline {
let border_color = outline_variant;
let border_y = if self.top {
rect.max.y
} else {
rect.min.y
};
ui.painter().line_segment(
[
egui::pos2(rect.min.x, border_y),
egui::pos2(rect.max.x, border_y),
],
Stroke::new(1.0, border_color),
);
}
let mut child_ui = ui.new_child(
egui::UiBuilder::new()
.max_rect(rect.shrink2(self.padding))
.layout(Layout::left_to_right(Align::Center))
);
child_ui.spacing_mut().item_spacing.x = self.item_spacing;
if self.tabbar {
let item_count = self.items.len();
if item_count > 0 {
let item_width = (rect.width() - self.padding.x * 2.0) / item_count as f32;
for item in self.items {
match item {
ToolbarItem::Widget(widget_fn) => {
child_ui.allocate_ui_with_layout(
Vec2::new(item_width, rect.height() - self.padding.y * 2.0),
Layout::centered_and_justified(egui::Direction::TopDown),
|ui| {
widget_fn(ui);
},
);
}
ToolbarItem::Spacer => {
}
}
}
}
} else {
for item in self.items {
match item {
ToolbarItem::Widget(widget_fn) => {
widget_fn(&mut child_ui);
}
ToolbarItem::Spacer => {
child_ui.with_layout(
Layout::right_to_left(Align::Center),
|_ui| {},
);
}
}
}
}
}
response
}
}
pub fn toolbar<'a>() -> MaterialToolbar<'a> {
MaterialToolbar::new()
}