Compare commits
5 Commits
7fdf6c6c83
...
3e4be0ea0b
Author | SHA1 | Date |
---|---|---|
mars | 3e4be0ea0b | |
mars | 58d9945e40 | |
mars | 97b993e470 | |
mars | 977ce92599 | |
mars | 9b711af557 |
101
src/anim.rs
101
src/anim.rs
|
@ -1,19 +1,24 @@
|
|||
use keyframe::EasingFunction;
|
||||
use crate::Color;
|
||||
|
||||
pub trait AnimationLerp<T> {
|
||||
fn lerp(&self, x: f32) -> T;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Animation<F> {
|
||||
pub struct Animation<F, T = f32> {
|
||||
time: f32,
|
||||
duration: f32,
|
||||
in_delay: f32,
|
||||
out_delay: f32,
|
||||
from: f32,
|
||||
to: f32,
|
||||
from: T,
|
||||
to: T,
|
||||
function: F,
|
||||
direction: bool,
|
||||
}
|
||||
|
||||
impl<F: EasingFunction> Animation<F> {
|
||||
pub fn new(function: F, duration: f32, from: f32, to: f32) -> Self {
|
||||
impl<F: EasingFunction, T: Copy> Animation<F, T> {
|
||||
pub fn new(function: F, duration: f32, from: T, to: T) -> Self {
|
||||
Self {
|
||||
time: duration,
|
||||
duration,
|
||||
|
@ -34,34 +39,6 @@ impl<F: EasingFunction> Animation<F> {
|
|||
self.time < self.duration
|
||||
}
|
||||
|
||||
pub fn get(&self) -> f32 {
|
||||
if self.is_active() {
|
||||
if self.time <= 0.0 {
|
||||
if self.direction {
|
||||
self.from
|
||||
} else {
|
||||
self.to
|
||||
}
|
||||
} else {
|
||||
let x = self.time / self.duration;
|
||||
let x = if self.direction { x } else { 1.0 - x };
|
||||
let lerp = self.function.y(x as f64) as f32;
|
||||
(1.0 - lerp) * self.from + lerp * self.to
|
||||
}
|
||||
} else if self.direction {
|
||||
self.to
|
||||
} else {
|
||||
self.from
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ease_to(&mut self, to: f32) {
|
||||
self.from = self.get();
|
||||
self.to = to;
|
||||
self.time = 0.0;
|
||||
self.direction = true;
|
||||
}
|
||||
|
||||
pub fn ease_in(&mut self) {
|
||||
if !self.direction {
|
||||
self.ease_toggle();
|
||||
|
@ -93,4 +70,62 @@ impl<F: EasingFunction> Animation<F> {
|
|||
pub fn set_out_delay(&mut self, delay: f32) {
|
||||
self.out_delay = delay;
|
||||
}
|
||||
|
||||
pub fn clamped(&self) -> Option<T> {
|
||||
if self.is_active() {
|
||||
if self.time <= 0.0 {
|
||||
if self.direction {
|
||||
Some(self.from)
|
||||
} else {
|
||||
Some(self.to)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if self.direction {
|
||||
Some(self.to)
|
||||
} else {
|
||||
Some(self.from)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: EasingFunction> AnimationLerp<f32> for Animation<F, f32> {
|
||||
fn lerp(&self, x: f32) -> f32 {
|
||||
(1.0 - x) * self.from + x * self.to
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: EasingFunction> AnimationLerp<Color> for Animation<F, Color> {
|
||||
fn lerp(&self, x: f32) -> Color {
|
||||
let from: glam::Vec4 = self.from.into();
|
||||
let to: glam::Vec4 = self.to.into();
|
||||
let lerp = (1.0 - x) * from + x * to;
|
||||
lerp.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T> Animation<F, T>
|
||||
where
|
||||
F: EasingFunction,
|
||||
T: Copy,
|
||||
Animation<F, T>: AnimationLerp<T>,
|
||||
{
|
||||
pub fn get(&self) -> T {
|
||||
if let Some(clamped) = self.clamped() {
|
||||
clamped
|
||||
} else {
|
||||
let x = self.time / self.duration;
|
||||
let x = if self.direction { x } else { 1.0 - x };
|
||||
let lerp = self.function.y(x as f64) as f32;
|
||||
self.lerp(lerp)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ease_to(&mut self, to: T) {
|
||||
self.from = self.get();
|
||||
self.to = to;
|
||||
self.time = 0.0;
|
||||
self.direction = true;
|
||||
}
|
||||
}
|
||||
|
|
26
src/lib.rs
26
src/lib.rs
|
@ -48,7 +48,31 @@ pub struct Color {
|
|||
pub a: f32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
impl Color {
|
||||
pub const WHITE: Self = Self::new(1., 1., 1., 1.);
|
||||
pub const BLACK: Self = Self::new(0., 0., 0., 1.);
|
||||
pub const TRANSPARENT: Self = Self::new(0., 0., 0., 0.);
|
||||
|
||||
pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||
Self { r, g, b, a }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<glam::Vec4> for Color {
|
||||
fn from(other: glam::Vec4) -> Self {
|
||||
let glam::Vec4 { x, y, z, w } = other;
|
||||
Self::new(x, y, z, w)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for glam::Vec4 {
|
||||
fn from(other: Color) -> Self {
|
||||
let Color { r, g, b, a } = other;
|
||||
Self::new(r, g, b, a)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum CursorEventKind {
|
||||
Hover,
|
||||
Select,
|
||||
|
|
400
src/widgets.rs
400
src/widgets.rs
|
@ -1,5 +1,5 @@
|
|||
use crate::anim::Animation;
|
||||
use crate::draw::DrawContext;
|
||||
use crate::draw::{Corner, DrawContext};
|
||||
use crate::{Color, CursorEventKind, Vec2};
|
||||
use keyframe::functions::*;
|
||||
|
||||
|
@ -35,9 +35,9 @@ impl Default for RoundButton {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
pos: Default::default(),
|
||||
radius: 0.05,
|
||||
spacing: 0.01,
|
||||
thickness: 0.005,
|
||||
radius: 0.02,
|
||||
spacing: 0.005,
|
||||
thickness: 0.001,
|
||||
shrink_anim: Animation::new(EaseOutQuint, 0.1, 1.0, 0.0),
|
||||
was_clicked: false,
|
||||
state: ButtonState::Idle,
|
||||
|
@ -60,7 +60,7 @@ impl Widget for RoundButton {
|
|||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
let color = Color {
|
||||
r: 1.0,
|
||||
g: 0.0,
|
||||
g: 1.0,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
@ -90,14 +90,106 @@ impl Widget for RoundButton {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct RectButton {
|
||||
pub pos: Vec2,
|
||||
pub size: Vec2,
|
||||
pub was_clicked: bool,
|
||||
pub is_selected: bool,
|
||||
pub is_hovering: bool,
|
||||
pub color_anim: Animation<EaseInQuad, Color>,
|
||||
}
|
||||
|
||||
impl Button for RectButton {
|
||||
fn was_clicked(&self) -> bool {
|
||||
self.was_clicked
|
||||
}
|
||||
}
|
||||
|
||||
impl RectButton {
|
||||
pub const INACTIVE_COLOR: Color = Color::new(1., 1., 1., 0.2);
|
||||
pub const HOVER_COLOR: Color = Color::new(1., 1., 1., 0.8);
|
||||
pub const SELECTED_COLOR: Color = Color::new(1., 1., 0., 1.);
|
||||
|
||||
pub fn new(pos: Vec2, size: Vec2) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
size,
|
||||
was_clicked: false,
|
||||
is_selected: false,
|
||||
is_hovering: false,
|
||||
color_anim: Animation::new(
|
||||
EaseInQuad,
|
||||
0.05,
|
||||
Self::INACTIVE_COLOR,
|
||||
Self::INACTIVE_COLOR,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_rect(self.pos, self.size, self.color_anim.get());
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||
let local = at - self.pos;
|
||||
let is_on = local.x > 0. && local.y > 0. && local.x < self.size.x && local.y < self.size.y;
|
||||
|
||||
match kind {
|
||||
CursorEventKind::Hover | CursorEventKind::Drag => {
|
||||
if is_on {
|
||||
if !self.is_hovering && !self.is_selected {
|
||||
self.color_anim.ease_to(Self::HOVER_COLOR);
|
||||
}
|
||||
|
||||
self.is_hovering = true;
|
||||
} else {
|
||||
if self.is_hovering && !self.is_selected {
|
||||
self.color_anim.ease_to(Self::INACTIVE_COLOR);
|
||||
}
|
||||
|
||||
self.is_hovering = false;
|
||||
}
|
||||
}
|
||||
CursorEventKind::Select => {
|
||||
if is_on {
|
||||
self.is_selected = true;
|
||||
self.color_anim.ease_to(Self::SELECTED_COLOR);
|
||||
}
|
||||
}
|
||||
CursorEventKind::Deselect => {
|
||||
if self.is_selected {
|
||||
self.was_clicked = true;
|
||||
self.is_selected = false;
|
||||
self.color_anim.ease_to(Self::HOVER_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub enum ScrollMenuState {
|
||||
Opening,
|
||||
Idle,
|
||||
Scrolling,
|
||||
Closing,
|
||||
Closed,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum ScrollMenuEvent {
|
||||
None,
|
||||
SubmenuOpen(usize),
|
||||
SubmenuClose(usize),
|
||||
}
|
||||
|
||||
pub struct ScrollMenuButton<T> {
|
||||
pub widget: T,
|
||||
pub slide_anim: Animation<EaseOut>,
|
||||
|
@ -109,7 +201,9 @@ pub struct ScrollMenu<T> {
|
|||
pub spacing: f32,
|
||||
pub scroll_anim: Animation<EaseInOutQuint>,
|
||||
pub state: ScrollMenuState,
|
||||
pub selected: usize,
|
||||
pub was_clicked: Option<usize>,
|
||||
pub event: ScrollMenuEvent,
|
||||
}
|
||||
|
||||
impl<T> ScrollMenu<T> {
|
||||
|
@ -122,8 +216,8 @@ impl<T> ScrollMenu<T> {
|
|||
.enumerate()
|
||||
.map(|(i, widget)| {
|
||||
let duration = 0.25;
|
||||
let in_delay = i as f32 * inter_button_delay;
|
||||
let out_delay = max_delay - in_delay;
|
||||
let out_delay = i as f32 * inter_button_delay;
|
||||
let in_delay = max_delay - out_delay;
|
||||
|
||||
let mut slide_anim = Animation::new(EaseOut, duration, 0.25, 0.0);
|
||||
slide_anim.set_in_delay(in_delay);
|
||||
|
@ -146,22 +240,32 @@ impl<T> ScrollMenu<T> {
|
|||
Self {
|
||||
buttons,
|
||||
spacing,
|
||||
scroll_anim: Animation::new(EaseInOutQuint, 0.2, 0.0, 0.0),
|
||||
scroll_anim: Animation::new(EaseInOutQuint, 0.25, 0.0, 0.0),
|
||||
state: ScrollMenuState::Opening,
|
||||
selected: 0,
|
||||
was_clicked: None,
|
||||
event: ScrollMenuEvent::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn was_clicked(&self) -> Option<usize> {
|
||||
pub fn get_event(&self) -> ScrollMenuEvent {
|
||||
self.event
|
||||
}
|
||||
|
||||
pub fn get_was_clicked(&self) -> Option<usize> {
|
||||
self.was_clicked
|
||||
}
|
||||
|
||||
pub fn scroll_to(&mut self, button_id: usize) {
|
||||
pub fn select(&mut self, button_id: usize) {
|
||||
self.scroll_anim.ease_to(button_id as f32);
|
||||
self.state = ScrollMenuState::Scrolling;
|
||||
self.event = ScrollMenuEvent::SubmenuClose(self.selected);
|
||||
self.selected = button_id;
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
self.state = ScrollMenuState::Closing;
|
||||
self.event = ScrollMenuEvent::SubmenuClose(self.selected);
|
||||
|
||||
for button in self.buttons.iter_mut() {
|
||||
button.slide_anim.ease_out();
|
||||
|
@ -171,7 +275,7 @@ impl<T> ScrollMenu<T> {
|
|||
|
||||
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 + button.slide_anim.get();
|
||||
let y = -(i as f32 - self.scroll_anim.get()) * self.spacing + button.slide_anim.get();
|
||||
cb(button, i, y);
|
||||
}
|
||||
}
|
||||
|
@ -194,19 +298,27 @@ impl<T> ScrollMenu<T> {
|
|||
|
||||
impl<T: Widget + Button> Widget for ScrollMenu<T> {
|
||||
fn update(&mut self, dt: f32) {
|
||||
self.event = ScrollMenuEvent::None;
|
||||
self.was_clicked = None;
|
||||
|
||||
match self.state {
|
||||
ScrollMenuState::Idle => {
|
||||
self.scroll_anim.update(dt);
|
||||
|
||||
self.for_buttons(|button, _i, _y| {
|
||||
button.widget.update(dt);
|
||||
});
|
||||
}
|
||||
ScrollMenuState::Opening => {
|
||||
if self.animate_buttons(dt) {
|
||||
self.state = ScrollMenuState::Idle;
|
||||
self.event = ScrollMenuEvent::SubmenuOpen(self.selected);
|
||||
}
|
||||
}
|
||||
ScrollMenuState::Idle => {}
|
||||
ScrollMenuState::Scrolling => {
|
||||
self.for_buttons(|button, _i, _y| {
|
||||
button.widget.update(dt);
|
||||
});
|
||||
|
||||
self.scroll_anim.update(dt);
|
||||
|
||||
if !self.scroll_anim.is_active() {
|
||||
self.state = ScrollMenuState::Idle;
|
||||
self.event = ScrollMenuEvent::SubmenuOpen(self.selected);
|
||||
}
|
||||
}
|
||||
ScrollMenuState::Closing => {
|
||||
|
@ -214,8 +326,14 @@ impl<T: Widget + Button> Widget for ScrollMenu<T> {
|
|||
self.state = ScrollMenuState::Closed;
|
||||
}
|
||||
}
|
||||
ScrollMenuState::Closed => {}
|
||||
ScrollMenuState::Closed => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.for_buttons(|button, _i, _y| {
|
||||
button.widget.update(dt);
|
||||
});
|
||||
}
|
||||
|
||||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
|
@ -223,18 +341,6 @@ impl<T: Widget + Button> Widget for ScrollMenu<T> {
|
|||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
|
@ -265,12 +371,112 @@ impl<T: Widget + Button> Widget for ScrollMenu<T> {
|
|||
}
|
||||
});
|
||||
|
||||
self.was_clicked = was_clicked;
|
||||
if let Some(clicked) = was_clicked {
|
||||
if clicked != self.selected {
|
||||
self.was_clicked = Some(clicked);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Offset<T> {
|
||||
inner: T,
|
||||
offset: Vec2,
|
||||
}
|
||||
|
||||
impl<T: Widget> Offset<T> {
|
||||
pub fn new(inner: T, offset: Vec2) -> Self {
|
||||
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 Reveal<T> {
|
||||
inner: T,
|
||||
slide_anim: Animation<EaseIn>,
|
||||
opacity_anim: Animation<EaseOut>,
|
||||
state: bool,
|
||||
}
|
||||
|
||||
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(EaseOut, duration, 0.0, 1.0),
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MainMenu {
|
||||
pub menu: ScrollMenu<RoundButton>,
|
||||
pub inventory: Reveal<Offset<Inventory>>,
|
||||
}
|
||||
|
||||
impl MainMenu {
|
||||
pub const SUBMENU_SPACING: f32 = 0.05;
|
||||
}
|
||||
|
||||
impl Default for MainMenu {
|
||||
|
@ -281,28 +487,148 @@ impl Default for MainMenu {
|
|||
buttons.push(button);
|
||||
}
|
||||
|
||||
let inventory = Inventory::new();
|
||||
let inventory = Offset::new(inventory, Vec2::new(Self::SUBMENU_SPACING, 0.0));
|
||||
let inventory = Reveal::new(inventory, -0.02, 0.1);
|
||||
|
||||
Self {
|
||||
menu: ScrollMenu::new(buttons, 0.2),
|
||||
menu: ScrollMenu::new(buttons, 0.1),
|
||||
inventory,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for MainMenu {
|
||||
fn update(&mut self, dt: f32) {
|
||||
match self.menu.was_clicked() {
|
||||
None => {},
|
||||
Some(0) => self.menu.close(),
|
||||
Some(button) => self.menu.scroll_to(button),
|
||||
match self.menu.get_was_clicked() {
|
||||
None => {}
|
||||
Some(4) => self.menu.close(),
|
||||
Some(button) => self.menu.select(button),
|
||||
};
|
||||
|
||||
match self.menu.get_event() {
|
||||
ScrollMenuEvent::SubmenuOpen(0) => self.inventory.show(),
|
||||
ScrollMenuEvent::SubmenuClose(0) => self.inventory.hide(),
|
||||
_ => {}
|
||||
};
|
||||
|
||||
self.menu.update(dt);
|
||||
self.inventory.update(dt);
|
||||
}
|
||||
|
||||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
self.menu.draw(&ctx);
|
||||
self.inventory.draw(&ctx);
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||
self.menu.on_cursor_event(kind, at);
|
||||
self.inventory.on_cursor_event(kind, at);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Inventory {
|
||||
tabs: Vec<RectButton>,
|
||||
}
|
||||
|
||||
impl Inventory {
|
||||
const HEAD_RADIUS: f32 = 0.025;
|
||||
const HEAD_HEIGHT: f32 = 0.03;
|
||||
const TAB_WIDTH: f32 = 0.04;
|
||||
const TAB_HEIGHT: f32 = 0.06;
|
||||
const TAB_NUM: usize = 6;
|
||||
const SEPARATOR_WIDTH: f32 = 0.015;
|
||||
const BOX_SIZE: f32 = 0.025;
|
||||
const BOX_MARGIN: f32 = 0.01;
|
||||
const BOX_PADDING: f32 = 0.005;
|
||||
const BOX_GRID_WIDTH: usize = 12;
|
||||
|
||||
pub fn new() -> Self {
|
||||
let tab_size = Vec2::new(Self::TAB_WIDTH, Self::TAB_HEIGHT);
|
||||
|
||||
let mut tabs = Vec::new();
|
||||
for i in 0..Self::TAB_NUM {
|
||||
let y = (i + 1) as f32 * Self::TAB_HEIGHT;
|
||||
let pos = Vec2::new(0.0, -y);
|
||||
tabs.push(RectButton::new(pos, tab_size));
|
||||
}
|
||||
|
||||
Self { tabs }
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Inventory {
|
||||
fn update(&mut self, dt: f32) {
|
||||
for tab in self.tabs.iter_mut() {
|
||||
tab.update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
for tab in self.tabs.iter_mut() {
|
||||
tab.draw(ctx);
|
||||
}
|
||||
|
||||
let head_color = Color {
|
||||
r: 1.0,
|
||||
g: 1.0,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
// alignment variables
|
||||
let tab_list_height = Self::TAB_NUM as f32 * Self::TAB_HEIGHT;
|
||||
let separator_xy = Vec2::new(Self::TAB_WIDTH, Self::SEPARATOR_WIDTH - tab_list_height);
|
||||
let separator_size = Vec2::new(
|
||||
Self::SEPARATOR_WIDTH,
|
||||
tab_list_height - Self::SEPARATOR_WIDTH,
|
||||
);
|
||||
let body_width = Self::BOX_GRID_WIDTH as f32 * (Self::BOX_SIZE + Self::BOX_PADDING)
|
||||
+ Self::BOX_MARGIN * 2.0;
|
||||
let body_height = tab_list_height - Self::BOX_MARGIN * 2.0;
|
||||
let head_width = Self::TAB_WIDTH + Self::SEPARATOR_WIDTH + body_width;
|
||||
let head_inner_xy = Vec2::ZERO;
|
||||
let head_inner_size = Vec2::new(head_width, Self::HEAD_HEIGHT - Self::HEAD_RADIUS);
|
||||
let head_edge_xy = Vec2::new(Self::HEAD_RADIUS, Self::HEAD_HEIGHT - Self::HEAD_RADIUS);
|
||||
let head_edge_size = Vec2::new(head_width - Self::HEAD_RADIUS * 2.0, Self::HEAD_RADIUS);
|
||||
let head_tl_xy = head_edge_xy;
|
||||
let head_tr_xy = head_tl_xy + Vec2::new(head_edge_size.x, 0.0);
|
||||
|
||||
// draw shapes
|
||||
ctx.draw_rect(separator_xy, separator_size, head_color);
|
||||
ctx.draw_quarter_circle(
|
||||
Corner::BottomRight,
|
||||
separator_xy,
|
||||
Self::SEPARATOR_WIDTH,
|
||||
head_color,
|
||||
);
|
||||
|
||||
ctx.draw_rect(head_inner_xy, head_inner_size, head_color);
|
||||
ctx.draw_rect(head_edge_xy, head_edge_size, head_color);
|
||||
ctx.draw_quarter_circle(Corner::TopLeft, head_tl_xy, Self::HEAD_RADIUS, head_color);
|
||||
ctx.draw_quarter_circle(Corner::TopRight, head_tr_xy, Self::HEAD_RADIUS, head_color);
|
||||
|
||||
// placeholder inventory item boxes
|
||||
let box_grid_stride = Self::BOX_SIZE + Self::BOX_PADDING;
|
||||
let box_grid_height = (body_height / box_grid_stride).floor() as usize;
|
||||
let box_rect_size = Vec2::new(Self::BOX_SIZE, Self::BOX_SIZE);
|
||||
let box_grid_xy = Vec2::new(
|
||||
Self::TAB_WIDTH + Self::SEPARATOR_WIDTH + Self::BOX_MARGIN,
|
||||
-Self::BOX_MARGIN - Self::BOX_SIZE,
|
||||
);
|
||||
let box_radius = 0.005;
|
||||
|
||||
for x in 0..Self::BOX_GRID_WIDTH {
|
||||
for y in 0..box_grid_height {
|
||||
let box_rect_xy = Vec2::new(x as f32, -(y as f32)) * box_grid_stride + box_grid_xy;
|
||||
ctx.draw_rounded_rect(box_rect_xy, box_rect_size, box_radius, head_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||
for tab in self.tabs.iter_mut() {
|
||||
tab.on_cursor_event(kind, at);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue