// Copyright (c) 2022 Marceline Cramer // SPDX-License-Identifier: AGPL-3.0-or-later use super::prelude::*; use std::ops::{Deref, DerefMut}; macro_rules! impl_shell_inner { ($shell:ident) => { impl Deref for $shell { type Target = T; fn deref(&self) -> &T { &self.inner } } impl DerefMut for $shell { fn deref_mut(&mut self) -> &mut T { &mut self.inner } } }; } pub struct Reveal { inner: T, slide_anim: Animation, opacity_anim: Animation, state: bool, } impl_shell_inner!(Reveal); impl Reveal { pub fn new(inner: T, slide: f32, duration: f32) -> Self { Self { inner, slide_anim: Animation::new(EaseIn, duration, slide, 0.0), opacity_anim: Animation::new(Linear, duration, 0, 0xff), state: false, } } pub fn get_offset(&self) -> Vec2 { Vec2::new(self.slide_anim.get(), 0.0) } pub fn show(&mut self) { self.state = true; self.slide_anim.ease_in(); self.opacity_anim.ease_in(); } pub fn hide(&mut self) { self.state = false; self.slide_anim.ease_out(); self.opacity_anim.ease_out(); } } impl Widget for Reveal { fn update(&mut self, dt: f32) { self.slide_anim.update(dt); self.opacity_anim.update(dt); self.inner.update(dt); } fn draw(&mut self, ctx: &DrawContext) { let ctx = ctx.with_offset(Vec2::new(self.slide_anim.get(), 0.0)); let ctx = if self.opacity_anim.is_active() { ctx.with_opacity(self.opacity_anim.get()) } else if self.state { ctx } else { return; }; self.inner.draw(&ctx); } fn on_cursor_event(&mut self, kind: CursorEventKind, mut at: Vec2) { if !self.state || self.opacity_anim.is_active() { return; } at -= Vec2::new(self.slide_anim.get(), 0.0); self.inner.on_cursor_event(kind, at); } } #[derive(Copy, Clone, Debug)] pub enum OffsetAlignment { Start, End, Center, Anchor, } impl OffsetAlignment { pub fn align(&self, start: f32, end: f32) -> f32 { match self { OffsetAlignment::Start => start, OffsetAlignment::End => end, OffsetAlignment::Center => (start + end) / 2.0, OffsetAlignment::Anchor => 0.0, } } } pub struct Offset { inner: T, offset: Vec2, } impl_shell_inner!(Offset); impl Offset { pub fn new(inner: T, offset: Vec2) -> Self { Self { inner, offset } } pub fn set_offset(&mut self, offset: Vec2) { self.offset = offset; } } impl Offset { pub fn new_aligned( inner: T, anchor: Vec2, hori_align: OffsetAlignment, vert_align: OffsetAlignment, ) -> Self { let bounds = inner.get_bounds(); let x = hori_align.align(bounds.tl.x, bounds.br.x); let y = vert_align.align(bounds.br.y, bounds.tl.y); let offset = anchor - Vec2::new(x, y); Self { inner, offset } } } impl Widget for Offset { fn update(&mut self, dt: f32) { self.inner.update(dt); } fn draw(&mut self, ctx: &DrawContext) { let ctx = ctx.with_offset(self.offset); self.inner.draw(&ctx); } fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) { let at = at - self.offset; self.inner.on_cursor_event(kind, at); } } pub struct Popup { inner: T, offset: Vec2, } impl_shell_inner!(Popup); impl Popup { pub fn new(inner: T, offset: Vec2) -> Self { Self { inner, offset } } pub fn set_offset(&mut self, offset: Vec2) { self.offset = offset; } } impl Popup { pub fn new_aligned( inner: T, anchor: Vec2, hori_align: OffsetAlignment, vert_align: OffsetAlignment, ) -> Self { let bounds = inner.get_bounds(); let x = hori_align.align(bounds.tl.x, bounds.br.x); let y = vert_align.align(bounds.br.y, bounds.tl.y); let offset = anchor - Vec2::new(x, y); Self { inner, offset } } } impl Widget for Popup { fn update(&mut self, dt: f32) { self.inner.update(dt); } fn draw(&mut self, ctx: &DrawContext) { let ctx = ctx.child_at(self.offset); self.inner.draw(&ctx); } fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) { let at = at - self.offset; self.inner.on_cursor_event(kind, at); } }