2022-07-21 21:19:57 +00:00
|
|
|
use super::*;
|
|
|
|
|
2022-07-27 06:08:10 +00:00
|
|
|
use text::Icon;
|
|
|
|
|
2022-07-21 21:19:57 +00:00
|
|
|
pub trait Button {
|
|
|
|
fn was_clicked(&self) -> bool;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum ButtonState {
|
|
|
|
Idle,
|
|
|
|
Clicking,
|
|
|
|
Clicked,
|
|
|
|
Releasing,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct RoundButton {
|
|
|
|
pos: Vec2,
|
|
|
|
radius: f32,
|
|
|
|
spacing: f32,
|
|
|
|
thickness: f32,
|
|
|
|
shrink_anim: Animation<EaseOutQuint>,
|
|
|
|
was_clicked: bool,
|
|
|
|
state: ButtonState,
|
2022-07-27 06:08:10 +00:00
|
|
|
icon: Option<Icon>,
|
2022-07-21 21:19:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl RoundButton {
|
2022-07-27 06:08:10 +00:00
|
|
|
pub fn new(icon: Option<LabelText>) -> Self {
|
|
|
|
let pos = Vec2::ZERO;
|
2022-07-21 21:19:57 +00:00
|
|
|
let radius = 0.05;
|
|
|
|
|
2022-07-27 06:08:10 +00:00
|
|
|
let icon = icon.map(|text| {
|
2022-07-21 21:19:57 +00:00
|
|
|
let scale = radius * 1.5;
|
2022-07-27 06:08:10 +00:00
|
|
|
let center = pos;
|
2022-07-21 21:19:57 +00:00
|
|
|
let color = Color::BLACK;
|
|
|
|
|
2022-07-27 06:08:10 +00:00
|
|
|
Icon::new(text, scale, color, center)
|
2022-07-21 21:19:57 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
Self {
|
2022-07-27 06:08:10 +00:00
|
|
|
pos,
|
2022-07-21 21:19:57 +00:00
|
|
|
radius,
|
|
|
|
spacing: 0.01,
|
|
|
|
thickness: 0.002,
|
|
|
|
shrink_anim: Animation::new(EaseOutQuint, 0.1, 1.0, 0.0),
|
|
|
|
was_clicked: false,
|
|
|
|
state: ButtonState::Idle,
|
2022-07-27 06:08:10 +00:00
|
|
|
icon,
|
2022-07-21 21:19:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Button for RoundButton {
|
|
|
|
fn was_clicked(&self) -> bool {
|
|
|
|
self.was_clicked
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Widget for RoundButton {
|
|
|
|
fn update(&mut self, dt: f32) {
|
|
|
|
self.shrink_anim.update(dt);
|
|
|
|
self.was_clicked = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn draw(&mut self, ctx: &DrawContext) {
|
|
|
|
let color = Color {
|
|
|
|
r: 1.0,
|
|
|
|
g: 1.0,
|
|
|
|
b: 1.0,
|
|
|
|
a: 1.0,
|
|
|
|
};
|
|
|
|
|
|
|
|
let spacing = self.shrink_anim.get() * self.spacing;
|
|
|
|
ctx.draw_circle(self.pos, self.radius, color);
|
|
|
|
ctx.draw_ring(self.pos, self.radius + spacing, self.thickness, color);
|
|
|
|
|
2022-07-27 06:08:10 +00:00
|
|
|
self.icon.draw(ctx);
|
2022-07-21 21:19:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
|
|
|
match kind {
|
|
|
|
CursorEventKind::Select => {
|
|
|
|
if self.pos.distance(at) < self.radius {
|
|
|
|
self.shrink_anim.ease_in();
|
|
|
|
self.state = ButtonState::Clicked;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CursorEventKind::Deselect => {
|
|
|
|
if self.state == ButtonState::Clicked {
|
|
|
|
self.shrink_anim.ease_out();
|
|
|
|
self.was_clicked = true;
|
|
|
|
self.state = ButtonState::Idle;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-27 05:09:52 +00:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct RectButtonStyle {
|
2022-07-21 21:19:57 +00:00
|
|
|
pub rounded_corners: CornerFlags,
|
|
|
|
pub radius: f32,
|
2022-07-27 06:08:10 +00:00
|
|
|
pub label_scale_factor: f32,
|
|
|
|
pub label_baseline: f32,
|
|
|
|
pub icon_scale_factor: f32,
|
|
|
|
pub icon_margin_factor: f32,
|
2022-07-27 05:09:52 +00:00
|
|
|
pub inactive_color: Color,
|
|
|
|
pub hover_color: Color,
|
|
|
|
pub selected_color: Color,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for RectButtonStyle {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
rounded_corners: CornerFlags::empty(),
|
|
|
|
radius: 0.0,
|
2022-07-27 06:08:10 +00:00
|
|
|
label_scale_factor: 0.65,
|
|
|
|
label_baseline: 0.25,
|
|
|
|
icon_scale_factor: 0.8,
|
|
|
|
icon_margin_factor: 1.1,
|
2022-07-27 05:09:52 +00:00
|
|
|
inactive_color: Color::new(1., 1., 1., 0.2),
|
|
|
|
hover_color: Color::new(1., 1., 1., 0.8),
|
|
|
|
selected_color: Color::new(1., 1., 0., 1.),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct RectButton {
|
|
|
|
pub style: RectButtonStyle,
|
|
|
|
pub rect: Rect,
|
2022-07-21 21:19:57 +00:00
|
|
|
pub was_clicked: bool,
|
|
|
|
pub is_selected: bool,
|
|
|
|
pub is_hovering: bool,
|
|
|
|
pub color_anim: Animation<EaseInQuad, Color>,
|
2022-07-27 04:44:53 +00:00
|
|
|
pub label: Option<Label>,
|
2022-07-27 06:08:10 +00:00
|
|
|
pub icon: Option<Icon>,
|
2022-07-21 21:19:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Button for RectButton {
|
|
|
|
fn was_clicked(&self) -> bool {
|
|
|
|
self.was_clicked
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RectButton {
|
2022-07-27 06:08:10 +00:00
|
|
|
pub fn new(style: RectButtonStyle, rect: Rect, label: Option<LabelText>, icon: Option<LabelText>) -> Self {
|
|
|
|
let mut label_left = rect.bl.x;
|
|
|
|
let mut alignment = HorizontalAlignment::Center;
|
|
|
|
|
|
|
|
let icon = icon.map(|text| {
|
|
|
|
let margin = rect.height() * style.icon_margin_factor;
|
|
|
|
label_left += margin;
|
|
|
|
alignment = HorizontalAlignment::Left;
|
|
|
|
let scale = rect.height() * style.icon_scale_factor;
|
|
|
|
let color = Color::BLACK;
|
|
|
|
let cx = rect.bl.x + margin / 2.0;
|
|
|
|
let cy = rect.bl.y + rect.height() / 2.0;
|
|
|
|
let center = Vec2::new(cx, cy);
|
|
|
|
|
|
|
|
Icon::new(text, scale, color, center)
|
|
|
|
});
|
2022-07-27 04:44:53 +00:00
|
|
|
|
|
|
|
let label = label.map(|text| {
|
2022-07-27 06:08:10 +00:00
|
|
|
let scale = rect.height() * style.label_scale_factor;
|
|
|
|
let left = label_left;
|
2022-07-27 04:44:53 +00:00
|
|
|
let right = rect.tr.x;
|
|
|
|
let baseline = rect.bl.y;
|
2022-07-27 06:08:10 +00:00
|
|
|
let baseline = (rect.height() * style.label_baseline) + baseline;
|
2022-07-27 04:44:53 +00:00
|
|
|
let color = Color::BLACK;
|
|
|
|
|
2022-07-27 06:08:10 +00:00
|
|
|
Label::new(text, alignment, scale, color, left, right, baseline)
|
2022-07-27 04:44:53 +00:00
|
|
|
});
|
|
|
|
|
2022-07-27 05:09:52 +00:00
|
|
|
let color_anim =
|
|
|
|
Animation::new(EaseInQuad, 0.05, style.inactive_color, style.inactive_color);
|
|
|
|
|
2022-07-21 21:19:57 +00:00
|
|
|
Self {
|
2022-07-27 05:09:52 +00:00
|
|
|
style,
|
2022-07-21 21:19:57 +00:00
|
|
|
rect,
|
|
|
|
was_clicked: false,
|
|
|
|
is_selected: false,
|
|
|
|
is_hovering: false,
|
2022-07-27 05:09:52 +00:00
|
|
|
color_anim,
|
2022-07-27 04:44:53 +00:00
|
|
|
label,
|
2022-07-27 06:08:10 +00:00
|
|
|
icon,
|
2022-07-21 21:19:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Widget for RectButton {
|
|
|
|
fn update(&mut self, dt: f32) {
|
|
|
|
self.was_clicked = false;
|
|
|
|
self.color_anim.update(dt);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn draw(&mut self, ctx: &DrawContext) {
|
|
|
|
ctx.draw_partially_rounded_rect(
|
2022-07-27 05:09:52 +00:00
|
|
|
self.style.rounded_corners,
|
2022-07-21 21:19:57 +00:00
|
|
|
self.rect,
|
2022-07-27 05:09:52 +00:00
|
|
|
self.style.radius,
|
2022-07-21 21:19:57 +00:00
|
|
|
self.color_anim.get(),
|
|
|
|
);
|
2022-07-27 04:44:53 +00:00
|
|
|
|
2022-07-27 05:09:52 +00:00
|
|
|
self.label.draw(ctx);
|
2022-07-27 06:08:10 +00:00
|
|
|
self.icon.draw(ctx);
|
2022-07-21 21:19:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
|
|
|
let is_on = self.rect.contains_point(at);
|
2022-07-27 05:09:52 +00:00
|
|
|
let style = &self.style;
|
2022-07-21 21:19:57 +00:00
|
|
|
|
|
|
|
match kind {
|
|
|
|
CursorEventKind::Hover | CursorEventKind::Drag => {
|
|
|
|
if is_on {
|
|
|
|
if !self.is_hovering && !self.is_selected {
|
2022-07-27 05:09:52 +00:00
|
|
|
self.color_anim.ease_to(style.hover_color);
|
2022-07-21 21:19:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.is_hovering = true;
|
|
|
|
} else {
|
|
|
|
if self.is_hovering && !self.is_selected {
|
2022-07-27 05:09:52 +00:00
|
|
|
self.color_anim.ease_to(style.inactive_color);
|
2022-07-21 21:19:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.is_hovering = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CursorEventKind::Select => {
|
|
|
|
if is_on {
|
|
|
|
self.is_selected = true;
|
2022-07-27 05:09:52 +00:00
|
|
|
self.color_anim.ease_to(style.selected_color);
|
2022-07-21 21:19:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
CursorEventKind::Deselect => {
|
|
|
|
if self.is_selected {
|
|
|
|
self.was_clicked = true;
|
|
|
|
self.is_selected = false;
|
|
|
|
|
|
|
|
if self.is_hovering {
|
2022-07-27 05:09:52 +00:00
|
|
|
self.color_anim.ease_to(style.hover_color);
|
2022-07-21 21:19:57 +00:00
|
|
|
} else {
|
2022-07-27 05:09:52 +00:00
|
|
|
self.color_anim.ease_to(style.inactive_color);
|
2022-07-21 21:19:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|