use crate::controls::MacInsets;
use crate::layout::subpixel_snap_rect;
use crate::theme::MacTheme;
use egui::{
CornerRadius, FontId, InnerResponse, Pos2, Rect, Response, Sense, Stroke, StrokeKind, Ui,
UiBuilder, Vec2, WidgetText,
};
pub struct MacBox<'a> {
title: Option<WidgetText>,
theme: Option<&'a MacTheme>,
fixed_rect: Option<Rect>,
}
impl<'a> MacBox<'a> {
pub fn new(title: impl Into<WidgetText>) -> Self {
Self {
title: Some(title.into()),
theme: None,
fixed_rect: None,
}
}
pub fn untitled() -> Self {
Self {
title: None,
theme: None,
fixed_rect: None,
}
}
pub fn theme(mut self, theme: &'a MacTheme) -> Self {
self.theme = Some(theme);
self
}
pub fn fixed_rect(mut self, rect: Rect) -> Self {
self.fixed_rect = Some(rect);
self
}
pub fn content_insets(&self) -> MacInsets {
let theme_cow = MacTheme::resolve_or_default(self.theme);
let m = theme_cow.box_view();
MacInsets {
top: m.inset_top.points(),
bottom: m.inset_bottom.points(),
left: m.inset_left.points(),
right: m.inset_right.points(),
}
}
pub fn corner_radius(&self) -> f32 {
let theme_cow = MacTheme::resolve_or_default(self.theme);
theme_cow.box_view().corner_radius.points()
}
pub fn border_width(&self) -> f32 {
let theme_cow = MacTheme::resolve_or_default(self.theme);
theme_cow.box_view().border_width.points()
}
pub fn paint_at(&self, ui: &mut Ui, rect: Rect) {
let theme_cow = MacTheme::resolve(self.theme, ui.ctx());
let theme: &MacTheme = &theme_cow;
let insets = self.content_insets();
let corner_r = self.corner_radius();
let border_w = self.border_width();
let m = theme.box_view();
let snapped = subpixel_snap_rect(rect, ui.ctx().pixels_per_point());
ui.painter().add(egui::epaint::RectShape::new(
snapped,
CornerRadius::same(corner_r as u8),
theme.box_background(),
Stroke::new(border_w, theme.box_border()),
StrokeKind::Inside,
));
if let Some(ref title) = self.title {
let font_id = FontId::proportional(m.title_font_size.points());
let galley = theme.layout_tracked_galley(
ui,
title.text(),
font_id,
theme.label(),
snapped.width(),
);
let title_pos = Pos2::new(
snapped.min.x + insets.left,
snapped.min.y + insets.top * 0.5 - galley.size().y * 0.5,
);
ui.painter().galley(title_pos, galley, theme.label());
}
}
pub fn show<R>(
self,
ui: &mut Ui,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> InnerResponse<R> {
let theme_cow = MacTheme::resolve(self.theme, ui.ctx());
let theme: &MacTheme = &theme_cow;
let insets = self.content_insets();
let corner_r = self.corner_radius();
let border_w = self.border_width();
let title_font_sz = theme.box_view().title_font_size.points();
let (start_pos, available_w, fixed_box_rect) = if let Some(fixed) = self.fixed_rect {
(fixed.min, fixed.width(), Some(fixed))
} else {
(ui.cursor().min, ui.available_width(), None)
};
let content_max_y = fixed_box_rect.map_or(f32::INFINITY, |r| r.max.y - insets.bottom);
let content_rect = Rect::from_min_max(
Pos2::new(start_pos.x + insets.left, start_pos.y + insets.top),
Pos2::new(start_pos.x + available_w - insets.right, content_max_y),
);
let bg_shape_idx = ui.painter().add(egui::Shape::Noop);
let box_scope_id = ui.next_auto_id().with("mac_box_content");
let mut child_ui = ui.new_child(
UiBuilder::new()
.id_salt(box_scope_id)
.max_rect(content_rect),
);
let inner = add_contents(&mut child_ui);
let content_min_rect = child_ui.min_rect();
let (rect, response) = if let Some(fixed) = fixed_box_rect {
let r = subpixel_snap_rect(fixed, ui.ctx().pixels_per_point());
let resp = ui.interact(r, box_scope_id.with("fixed_box"), Sense::hover());
(r, resp)
} else {
let box_h = insets.top + content_min_rect.height() + insets.bottom;
let box_rect = Rect::from_min_size(start_pos, Vec2::new(available_w, box_h));
let box_rect = subpixel_snap_rect(box_rect, ui.ctx().pixels_per_point());
ui.allocate_exact_size(box_rect.size(), Sense::hover())
};
ui.painter().set(
bg_shape_idx,
egui::epaint::RectShape::new(
rect,
CornerRadius::same(corner_r as u8),
theme.box_background(),
Stroke::new(border_w, theme.box_border()),
StrokeKind::Inside,
),
);
if let Some(title) = self.title {
let galley = theme.layout_tracked_galley(
ui,
title.text(),
FontId::proportional(title_font_sz),
theme.label(),
available_w,
);
let title_pos = Pos2::new(
rect.min.x + insets.left,
rect.min.y + insets.top * 0.5 - galley.size().y * 0.5,
);
ui.painter().galley(title_pos, galley, theme.label());
}
InnerResponse::new(inner, response)
}
pub fn separator(ui: &mut Ui) -> Response {
let theme = MacTheme::resolve(None, ui.ctx());
let frame_h = theme.box_view().separator_frame_height.points();
let border_w = theme.box_view().border_width.points();
let (rect, response) =
ui.allocate_exact_size(Vec2::new(ui.available_width(), frame_h), Sense::hover());
let painter = ui.painter_at(rect);
let cy = rect.center().y;
painter.line_segment(
[Pos2::new(rect.min.x, cy), Pos2::new(rect.max.x, cy)],
Stroke::new(border_w, theme.separator()),
);
response
}
}