use super::MacDisclosureGroup;
use crate::controls::MacControlSize;
use crate::motion::CASpring;
use crate::theme::MacTheme;
use egui::{CornerRadius, Pos2, Response, Sense, Shape, Stroke, StrokeKind, Ui, Vec2, Widget};
pub struct MacDisclosureButton<'a> {
is_expanded: &'a mut bool,
size: MacControlSize,
theme: Option<&'a MacTheme>,
}
impl<'a> MacDisclosureButton<'a> {
pub fn new(is_expanded: &'a mut bool) -> Self {
Self {
is_expanded,
size: MacControlSize::Regular,
theme: None,
}
}
pub fn control_size(mut self, size: MacControlSize) -> Self {
self.size = size;
self
}
pub fn theme(mut self, theme: &'a MacTheme) -> Self {
self.theme = Some(theme);
self
}
pub fn frame_size(&self) -> Vec2 {
let theme_cow = MacTheme::resolve_or_default(self.theme);
let m = theme_cow.disclosure_group();
match self.size {
MacControlSize::Regular | MacControlSize::Large => {
Vec2::new(m.button_regular_width.points(), m.button_regular_height.points())
}
MacControlSize::Small | MacControlSize::CompactInfobox => {
Vec2::new(m.button_small_width.points(), m.button_small_height.points())
}
MacControlSize::Mini => {
Vec2::new(m.button_mini_width.points(), m.button_mini_height.points())
}
}
}
pub fn intrinsic_size(&self) -> Vec2 {
let theme_cow = MacTheme::resolve_or_default(self.theme);
let m = theme_cow.disclosure_group();
match self.size {
MacControlSize::Regular | MacControlSize::Large => {
let sz = m.button_regular_intrinsic.points();
Vec2::new(sz, sz)
}
MacControlSize::Small | MacControlSize::CompactInfobox => {
let sz = m.button_small_intrinsic.points();
Vec2::new(sz, sz)
}
MacControlSize::Mini => {
let sz = m.button_mini_intrinsic.points();
Vec2::new(sz, sz)
}
}
}
}
impl<'a> Widget for MacDisclosureButton<'a> {
fn ui(self, ui: &mut Ui) -> Response {
let theme_cow = MacTheme::resolve(self.theme, ui.ctx());
let theme: &MacTheme = &theme_cow;
let metrics = theme.disclosure_group();
let desired_size = self.frame_size();
let (rect, mut response) = ui.allocate_exact_size(desired_size, Sense::click());
if response.clicked() {
*self.is_expanded = !*self.is_expanded;
response.mark_changed();
}
if ui.is_rect_visible(rect) {
let painter = ui.painter_at(rect);
let corner_r = rect.height() * 0.5;
let fill = if response.is_pointer_button_down_on() {
theme.selected_control()
} else if response.hovered() {
theme.control()
} else {
theme.control_background()
};
painter.rect(
rect,
CornerRadius::same(corner_r as u8),
fill,
Stroke::new(1.0, theme.separator()),
StrokeKind::Inside,
);
let spring = CASpring::from_physical(
metrics.spring_mass.points(),
metrics.spring_stiffness.points(),
metrics.spring_damping.points(),
);
let target_angle = MacDisclosureGroup::rotation_angle(*self.is_expanded);
let anim_angle = if ui.is_rect_visible(rect) && crate::is_window_active(ui.ctx()) {
spring.animate_f32(ui.ctx(), response.id.with("pill_rot"), target_angle)
} else {
target_angle
};
let center = rect.center();
let chevron_sz = metrics.chevron_size.points() * 0.5;
let cos = anim_angle.cos();
let sin = anim_angle.sin();
let rotate = |ox: f32, oy: f32| -> Pos2 {
let rx = ox * cos - oy * sin;
let ry = ox * sin + oy * cos;
Pos2::new(center.x + rx, center.y + ry)
};
let p1 = rotate(-chevron_sz * 0.5, -chevron_sz);
let p2 = rotate(-chevron_sz * 0.5, chevron_sz);
let p3 = rotate(chevron_sz, 0.0);
let glyph_color = if response.hovered() {
theme.label()
} else {
theme.secondary_label()
};
painter.add(Shape::convex_polygon(
vec![p1, p2, p3],
glyph_color,
Stroke::NONE,
));
}
response
}
}