canary-rs/scripts/sao-ui/src/widgets/shell.rs

205 lines
4.7 KiB
Rust

// 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<T> Deref for $shell<T> {
type Target = T;
fn deref(&self) -> &T {
&self.inner
}
}
impl<T> DerefMut for $shell<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.inner
}
}
};
}
pub struct Reveal<T> {
inner: T,
slide_anim: Animation<EaseIn>,
opacity_anim: Animation<Linear, u8>,
state: bool,
}
impl_shell_inner!(Reveal);
impl<T: Widget> Reveal<T> {
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<T: Widget> Widget for Reveal<T> {
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<T> {
inner: T,
offset: Vec2,
}
impl_shell_inner!(Offset);
impl<T> Offset<T> {
pub fn new(inner: T, offset: Vec2) -> Self {
Self { inner, offset }
}
pub fn set_offset(&mut self, offset: Vec2) {
self.offset = offset;
}
}
impl<T: RectBounds> Offset<T> {
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<T: Widget> Widget for Offset<T> {
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<T> {
inner: T,
offset: Vec2,
}
impl_shell_inner!(Popup);
impl<T> Popup<T> {
pub fn new(inner: T, offset: Vec2) -> Self {
Self { inner, offset }
}
pub fn set_offset(&mut self, offset: Vec2) {
self.offset = offset;
}
}
impl<T: RectBounds> Popup<T> {
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<T: Widget> Widget for Popup<T> {
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);
}
}