egui_macview/surfaces/
mod.rs1pub mod card;
7pub mod hud;
8pub mod popover;
9pub use card::MacCard;
10pub use hud::MacFloatingInfoBox;
11pub use popover::{MacPopover, MacPopoverArrowEdge, MacPopoverSurface};
12
13use crate::theme::MacTheme;
14use egui::{CornerRadius, Id, Pos2, Rect, Response, Stroke, StrokeKind, Ui, Vec2};
15use std::hash::Hash;
16
17#[derive(Default)]
42pub struct MacPanel<'a> {
43 id: Option<Id>,
44 title: Option<String>,
45 theme: Option<&'a MacTheme>,
46 rounding: Option<u8>,
47}
48
49impl<'a> MacPanel<'a> {
50 pub fn new() -> Self {
52 Self::default()
53 }
54
55 pub fn id(mut self, id_source: impl Hash + std::fmt::Debug) -> Self {
57 self.id = Some(Id::NULL.with(id_source));
58 self
59 }
60
61 pub fn theme(mut self, theme: &'a MacTheme) -> Self {
63 self.theme = Some(theme);
64 self
65 }
66
67 pub fn title(mut self, title: impl Into<String>) -> Self {
69 self.title = Some(title.into());
70 self
71 }
72
73 pub fn rounding(mut self, r: u8) -> Self {
75 self.rounding = Some(r);
76 self
77 }
78
79 pub fn show<R>(
81 self,
82 ui: &mut Ui,
83 rect: Rect,
84 add_contents: impl FnOnce(&mut Ui) -> R,
85 ) -> (Response, R) {
86 let theme_cow = MacTheme::resolve(self.theme, ui.ctx());
87 let theme: &MacTheme = &theme_cow;
88
89 let panel_id = self.id.unwrap_or_else(|| {
90 ui.scope_id().with((
91 "mac_panel",
92 self.title.as_deref().unwrap_or(""),
93 rect.min.x.to_bits(),
94 rect.min.y.to_bits(),
95 ))
96 });
97 let resp = ui.interact(rect, panel_id, egui::Sense::click_and_drag());
98 if ui.rect_contains_pointer(rect) {
99 ui.input_mut(|i| {
100 i.smooth_scroll_delta = Vec2::ZERO;
101 });
102 }
103
104 let popover_metrics = theme.popover_metrics();
105 let rounding = self
106 .rounding
107 .unwrap_or_else(|| popover_metrics.corner_radius.points() as u8);
108
109 let bg_color = theme.window_background();
110 let border_stroke = Stroke::new(popover_metrics.border_width.points(), theme.separator());
111
112 let painter = ui.painter_at(rect);
113 painter.rect(
114 rect,
115 CornerRadius::same(rounding),
116 bg_color,
117 border_stroke,
118 StrokeKind::Inside,
119 );
120
121 let header_height = popover_metrics.header_height.points();
122
123 let content_rect = if let Some(title) = self.title {
124 let title_rect = Rect::from_min_size(rect.min, Vec2::new(rect.width(), header_height));
125 painter.line_segment(
126 [
127 Pos2::new(rect.min.x, rect.min.y + header_height),
128 Pos2::new(rect.max.x, rect.min.y + header_height),
129 ],
130 border_stroke,
131 );
132 painter.text(
133 title_rect.center(),
134 egui::Align2::CENTER_CENTER,
135 title,
136 egui::FontId::proportional(popover_metrics.header_font_size.points()),
137 theme.label(),
138 );
139 Rect::from_min_max(Pos2::new(rect.min.x, rect.min.y + header_height), rect.max)
140 } else {
141 rect
142 };
143
144 let mut child_ui = ui.new_child(
145 egui::UiBuilder::new()
146 .id_salt((panel_id, "mac_panel_content"))
147 .max_rect(content_rect)
148 .layout(egui::Layout::top_down(egui::Align::Min)),
149 );
150 let ret = add_contents(&mut child_ui);
151 (resp, ret)
152 }
153}