Main menu + deref draw function args

This commit is contained in:
mars 2022-07-09 16:56:19 -06:00
parent dd34f5110c
commit 1a9b1fd9c3
5 changed files with 150 additions and 44 deletions

View File

@ -94,7 +94,7 @@ impl UiPanel {
unsafe { UiPanel_setColor(self.0, color.r, color.g, color.b, color.a) }
}
pub fn draw_triangle(&self, v1: &Vec2, v2: &Vec2, v3: &Vec2, color: &Color) {
pub fn draw_triangle(&self, v1: Vec2, v2: Vec2, v3: Vec2, color: Color) {
unsafe {
UiPanel_drawTriangle(
self.0, v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, color.r, color.g, color.b, color.a,

View File

@ -1,5 +1,6 @@
use keyframe::EasingFunction;
#[derive(Clone)]
pub struct Animation<F> {
time: f32,
duration: f32,
@ -42,11 +43,10 @@ impl<F: EasingFunction> Animation<F> {
}
}
pub fn ease_to(&mut self, duration: f32, to: f32) {
pub fn ease_to(&mut self, to: f32) {
self.from = self.get();
self.to = to;
self.time = 0.0;
self.duration = duration;
self.direction = true;
}

View File

@ -1,20 +1,27 @@
use crate::{Color, Vec2};
use crate::abi::UiPanel;
use crate::{Color, Vec2};
pub struct DrawContext {
panel: UiPanel,
offset: Vec2,
}
impl DrawContext {
pub fn new(panel: UiPanel) -> Self {
Self { panel }
Self {
panel,
offset: Vec2::ZERO,
}
}
pub fn draw_triangle(&self, v1: &Vec2, v2: &Vec2, v3: &Vec2, color: &Color) {
self.panel.draw_triangle(v1, v2, v3, color)
pub fn draw_triangle(&self, v1: Vec2, v2: Vec2, v3: Vec2, color: Color) {
let v1 = v1 + self.offset;
let v2 = v2 + self.offset;
let v3 = v3 + self.offset;
self.panel.draw_triangle(v1, v2, v3, color);
}
pub fn draw_circle(&self, center: &Vec2, radius: f32, color: &Color) {
pub fn draw_circle(&self, center: Vec2, radius: f32, color: Color) {
use std::f32::consts::PI;
let delta = PI / 16.0;
@ -23,14 +30,14 @@ impl DrawContext {
let mut last_spoke = Vec2::new(radius + center.x, center.y);
let mut theta = delta;
while theta < limit {
let new_spoke = Vec2::from_angle(theta) * radius + *center;
self.draw_triangle(&center, &last_spoke, &new_spoke, &color);
let new_spoke = Vec2::from_angle(theta) * radius + center;
self.draw_triangle(center, last_spoke, new_spoke, color);
last_spoke = new_spoke;
theta += delta;
}
}
pub fn draw_ring(&self, center: &Vec2, radius: f32, thickness: f32, color: &Color) {
pub fn draw_ring(&self, center: Vec2, radius: f32, thickness: f32, color: Color) {
use std::f32::consts::PI;
let delta = PI / 64.0;
@ -41,12 +48,12 @@ impl DrawContext {
let mut theta = delta;
while theta < limit {
let angle = Vec2::from_angle(theta);
let new_spoke = angle * radius + *center;
let new_spoke2 = angle * (radius + thickness) + *center;
let last_spoke2 = Vec2::from_angle(last_theta) * (radius + thickness) + *center;
let new_spoke = angle * radius + center;
let new_spoke2 = angle * (radius + thickness) + center;
let last_spoke2 = Vec2::from_angle(last_theta) * (radius + thickness) + center;
self.draw_triangle(&new_spoke2, &last_spoke, &new_spoke, &color);
self.draw_triangle(&new_spoke2, &last_spoke2, &last_spoke, &color);
self.draw_triangle(new_spoke2, last_spoke, new_spoke, color);
self.draw_triangle(new_spoke2, last_spoke2, last_spoke, color);
last_spoke = new_spoke;
last_theta = theta;
@ -54,13 +61,20 @@ impl DrawContext {
}
}
pub fn draw_rect(&self, xy: &Vec2, size: &Vec2, color: &Color) {
pub fn draw_rect(&self, xy: &Vec2, size: &Vec2, color: Color) {
let v1 = *xy;
let v2 = v1 + Vec2::new(size.x, 0.0);
let v3 = v1 + Vec2::new(0.0, size.y);
let v4 = v1 + *size;
self.draw_triangle(&v1, &v2, &v3, &color);
self.draw_triangle(&v2, &v3, &v4, &color);
self.draw_triangle(v1, v2, v3, color);
self.draw_triangle(v2, v3, v4, color);
}
pub fn with_offset(&self, offset: Vec2) -> Self {
Self {
panel: self.panel,
offset: self.offset + offset,
}
}
}

View File

@ -14,36 +14,28 @@ use widgets::Widget;
pub struct DummyPanel {
panel: UiPanel,
buttons: Vec<widgets::RoundButton>,
menu: widgets::MainMenu,
}
impl DummyPanel {
fn bind(panel: UiPanel) -> Self {
let mut buttons = Vec::new();
for i in 0..6 {
let mut button = widgets::RoundButton::default();
button.pos.y = i as f32 * 0.2;
buttons.push(button);
Self {
panel,
menu: widgets::MainMenu::default(),
}
Self { panel, buttons }
}
}
impl abi::PanelImpl for DummyPanel {
fn update(&mut self, dt: f32) {
self.menu.update(dt);
let ctx = draw::DrawContext::new(self.panel);
for button in self.buttons.iter_mut() {
button.update(dt);
button.draw(&ctx);
}
self.menu.draw(&ctx);
}
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
for button in self.buttons.iter_mut() {
button.on_cursor_event(kind, at);
}
self.menu.on_cursor_event(kind, at);
}
}

View File

@ -1,7 +1,7 @@
use crate::anim::Animation;
use keyframe::functions::*;
use crate::draw::DrawContext;
use crate::{Color, CursorEventKind, Vec2};
use keyframe::functions::*;
pub trait Widget {
fn update(&mut self, dt: f32);
@ -9,12 +9,22 @@ pub trait Widget {
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2);
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ButtonState {
Idle,
Clicking,
Clicked,
Releasing,
}
pub struct RoundButton {
pub pos: Vec2,
pub radius: f32,
pub spacing: f32,
pub thickness: f32,
pub shrink_anim: Animation<EaseInQuint>,
pub shrink_anim: Animation<EaseOutQuint>,
pub was_clicked: bool,
pub state: ButtonState,
}
impl Default for RoundButton {
@ -24,14 +34,23 @@ impl Default for RoundButton {
radius: 0.05,
spacing: 0.01,
thickness: 0.005,
shrink_anim: Animation::new(EaseInQuint, 0.1, 1.0, 0.0),
shrink_anim: Animation::new(EaseOutQuint, 0.1, 1.0, 0.0),
was_clicked: false,
state: ButtonState::Idle,
}
}
}
impl RoundButton {
pub 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) {
@ -43,19 +62,100 @@ impl Widget for RoundButton {
};
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);
ctx.draw_circle(self.pos, self.radius, color);
ctx.draw_ring(self.pos, self.radius + spacing, self.thickness, color);
}
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();
},
CursorEventKind::Deselect => {
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;
}
_ => {}
}
}
}
pub struct MainMenu {
pub buttons: Vec<RoundButton>,
pub spacing: f32,
pub scroll_anim: Animation<EaseInOutQuint>,
}
impl Default for MainMenu {
fn default() -> Self {
let mut buttons = Vec::new();
for _ in 0..5 {
buttons.push(RoundButton::default());
}
Self {
buttons,
spacing: 0.2,
scroll_anim: Animation::new(EaseInOutQuint, 0.2, 0.0, 0.0),
}
}
}
impl MainMenu {
pub fn for_buttons(&mut self, mut cb: impl FnMut(&mut RoundButton, usize, f32)) {
for (i, button) in self.buttons.iter_mut().enumerate() {
let y = (i as f32 - self.scroll_anim.get()) * self.spacing;
cb(button, i, y);
}
}
}
impl Widget for MainMenu {
fn update(&mut self, dt: f32) {
self.scroll_anim.update(dt);
let mut scroll_to = None;
self.for_buttons(|button, i, _y| {
if button.was_clicked() {
scroll_to = Some(i);
}
button.update(dt);
});
if let Some(to) = scroll_to {
self.scroll_anim.ease_to(to as f32);
}
}
fn draw(&mut self, ctx: &DrawContext) {
ctx.draw_triangle(
Vec2::new(self.spacing, 0.0),
Vec2::new(self.spacing + 0.05, 0.05),
Vec2::new(self.spacing + 0.05, -0.05),
Color {
r: 1.0,
g: 1.0,
b: 1.0,
a: 1.0,
},
);
self.for_buttons(|button, _i, y| {
let ctx = ctx.with_offset(Vec2::new(0.0, y));
button.draw(&ctx);
})
}
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
self.for_buttons(|button, _i, y| {
let at = at - Vec2::new(0.0, y);
button.on_cursor_event(kind, at);
})
}
}