Add ScrollMenu
This commit is contained in:
parent
a962886de2
commit
cc4f064587
166
src/widgets.rs
166
src/widgets.rs
|
@ -9,6 +9,10 @@ pub trait Widget {
|
|||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2);
|
||||
}
|
||||
|
||||
pub trait Button {
|
||||
fn was_clicked(&self) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ButtonState {
|
||||
Idle,
|
||||
|
@ -41,8 +45,8 @@ impl Default for RoundButton {
|
|||
}
|
||||
}
|
||||
|
||||
impl RoundButton {
|
||||
pub fn was_clicked(&self) -> bool {
|
||||
impl Button for RoundButton {
|
||||
fn was_clicked(&self) -> bool {
|
||||
self.was_clicked
|
||||
}
|
||||
}
|
||||
|
@ -74,62 +78,114 @@ impl Widget for RoundButton {
|
|||
self.state = ButtonState::Clicked;
|
||||
}
|
||||
}
|
||||
CursorEventKind::Deselect => if self.state == ButtonState::Clicked {
|
||||
self.shrink_anim.ease_out();
|
||||
self.was_clicked = true;
|
||||
self.state = ButtonState::Idle;
|
||||
CursorEventKind::Deselect => {
|
||||
if self.state == ButtonState::Clicked {
|
||||
self.shrink_anim.ease_out();
|
||||
self.was_clicked = true;
|
||||
self.state = ButtonState::Idle;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MainMenu {
|
||||
pub buttons: Vec<RoundButton>,
|
||||
pub spacing: f32,
|
||||
pub scroll_anim: Animation<EaseInOutQuint>,
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub enum ScrollMenuState {
|
||||
Opening,
|
||||
Idle,
|
||||
}
|
||||
|
||||
impl Default for MainMenu {
|
||||
fn default() -> Self {
|
||||
let mut buttons = Vec::new();
|
||||
for _ in 0..5 {
|
||||
buttons.push(RoundButton::default());
|
||||
}
|
||||
pub struct ScrollMenuButton<T> {
|
||||
pub widget: T,
|
||||
pub slide_anim: Animation<EaseOut>,
|
||||
pub opacity_anim: Animation<EaseOut>,
|
||||
}
|
||||
|
||||
pub struct ScrollMenu<T> {
|
||||
pub buttons: Vec<ScrollMenuButton<T>>,
|
||||
pub spacing: f32,
|
||||
pub scroll_anim: Animation<EaseInOutQuint>,
|
||||
pub state: ScrollMenuState,
|
||||
}
|
||||
|
||||
impl<T> ScrollMenu<T> {
|
||||
pub fn new(buttons: Vec<T>, spacing: f32) -> Self {
|
||||
let buttons: Vec<_> = buttons
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, widget)| {
|
||||
let duration = 0.25;
|
||||
let delay = i as f32 * 0.05;
|
||||
|
||||
let mut slide_anim = Animation::new(EaseOut, duration, 0.25, 0.0);
|
||||
slide_anim.set_delay(delay);
|
||||
slide_anim.ease_in();
|
||||
|
||||
let mut opacity_anim = Animation::new(EaseOut, duration, 0.0, 1.0);
|
||||
opacity_anim.set_delay(delay);
|
||||
opacity_anim.ease_in();
|
||||
|
||||
ScrollMenuButton {
|
||||
widget,
|
||||
slide_anim,
|
||||
opacity_anim,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
buttons,
|
||||
spacing: 0.2,
|
||||
spacing,
|
||||
scroll_anim: Animation::new(EaseInOutQuint, 0.2, 0.0, 0.0),
|
||||
state: ScrollMenuState::Opening,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MainMenu {
|
||||
pub fn for_buttons(&mut self, mut cb: impl FnMut(&mut RoundButton, usize, f32)) {
|
||||
pub fn for_buttons(&mut self, mut cb: impl FnMut(&mut ScrollMenuButton<T>, usize, f32)) {
|
||||
for (i, button) in self.buttons.iter_mut().enumerate() {
|
||||
let y = (i as f32 - self.scroll_anim.get()) * self.spacing;
|
||||
let y = (i as f32 - self.scroll_anim.get()) * self.spacing + button.slide_anim.get();
|
||||
cb(button, i, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for MainMenu {
|
||||
impl<T: Widget + Button> Widget for ScrollMenu<T> {
|
||||
fn update(&mut self, dt: f32) {
|
||||
self.scroll_anim.update(dt);
|
||||
match self.state {
|
||||
ScrollMenuState::Idle => {
|
||||
self.scroll_anim.update(dt);
|
||||
|
||||
let mut scroll_to = None;
|
||||
let mut scroll_to = None;
|
||||
|
||||
self.for_buttons(|button, i, _y| {
|
||||
if button.was_clicked() {
|
||||
scroll_to = Some(i);
|
||||
self.for_buttons(|button, i, _y| {
|
||||
if button.widget.was_clicked() {
|
||||
scroll_to = Some(i);
|
||||
}
|
||||
|
||||
button.widget.update(dt);
|
||||
});
|
||||
|
||||
if let Some(to) = scroll_to {
|
||||
self.scroll_anim.ease_to(to as f32);
|
||||
}
|
||||
}
|
||||
ScrollMenuState::Opening => {
|
||||
let mut all_slid = true;
|
||||
|
||||
button.update(dt);
|
||||
});
|
||||
self.for_buttons(|button, _i, _y| {
|
||||
button.slide_anim.update(dt);
|
||||
button.opacity_anim.update(dt);
|
||||
|
||||
if let Some(to) = scroll_to {
|
||||
self.scroll_anim.ease_to(to as f32);
|
||||
if button.slide_anim.is_active() {
|
||||
all_slid = false;
|
||||
}
|
||||
});
|
||||
|
||||
if all_slid {
|
||||
self.state = ScrollMenuState::Idle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,14 +204,58 @@ impl Widget for MainMenu {
|
|||
|
||||
self.for_buttons(|button, _i, y| {
|
||||
let ctx = ctx.with_offset(Vec2::new(0.0, y));
|
||||
button.draw(&ctx);
|
||||
|
||||
let opacity = button.opacity_anim.get();
|
||||
let ctx = if opacity != 1.0 {
|
||||
ctx.with_opacity(opacity)
|
||||
} else {
|
||||
ctx
|
||||
};
|
||||
|
||||
button.widget.draw(&ctx);
|
||||
})
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||
if self.state != ScrollMenuState::Idle {
|
||||
return;
|
||||
}
|
||||
|
||||
self.for_buttons(|button, _i, y| {
|
||||
let at = at - Vec2::new(0.0, y);
|
||||
button.on_cursor_event(kind, at);
|
||||
button.widget.on_cursor_event(kind, at);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MainMenu {
|
||||
pub menu: ScrollMenu<RoundButton>,
|
||||
}
|
||||
|
||||
impl Default for MainMenu {
|
||||
fn default() -> Self {
|
||||
let mut buttons = Vec::new();
|
||||
for _ in 0..5 {
|
||||
let button = RoundButton::default();
|
||||
buttons.push(button);
|
||||
}
|
||||
|
||||
Self {
|
||||
menu: ScrollMenu::new(buttons, 0.2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for MainMenu {
|
||||
fn update(&mut self, dt: f32) {
|
||||
self.menu.update(dt);
|
||||
}
|
||||
|
||||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
self.menu.draw(&ctx);
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||
self.menu.on_cursor_event(kind, at);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue