Compare commits
6 Commits
3e4be0ea0b
...
9d83792ef7
Author | SHA1 | Date |
---|---|---|
mars | 9d83792ef7 | |
mars | 4e78cfb3d4 | |
mars | 87ec9f0f52 | |
mars | 9f52ff584f | |
mars | f7c2a24572 | |
mars | bfe8ca07a0 |
|
@ -7,10 +7,11 @@ edition = "2021"
|
|||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
bitflags = "^1"
|
||||
glam = "^0.21"
|
||||
keyframe = "1"
|
||||
wee_alloc = "^0.4"
|
||||
|
||||
[profile.release]
|
||||
opt-level = "s"
|
||||
opt-level = 3
|
||||
lto = "fat"
|
||||
|
|
228
src/draw.rs
228
src/draw.rs
|
@ -1,5 +1,6 @@
|
|||
use crate::abi::UiPanel;
|
||||
use crate::{Color, Vec2};
|
||||
use bitflags::bitflags;
|
||||
|
||||
pub enum Corner {
|
||||
TopRight,
|
||||
|
@ -8,9 +9,102 @@ pub enum Corner {
|
|||
TopLeft,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct CornerFlags: u8 {
|
||||
const TOP_RIGHT = 0x01;
|
||||
const BOTTOM_RIGHT = 0x02;
|
||||
const BOTTOM_LEFT = 0x04;
|
||||
const TOP_LEFT = 0x08;
|
||||
const TOP = 0x09;
|
||||
const RIGHT = 0x03;
|
||||
const BOTTOM = 0x06;
|
||||
const LEFT = 0x0C;
|
||||
const ALL = 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ColoredTriangle {
|
||||
pub v1: Vec2,
|
||||
pub v2: Vec2,
|
||||
pub v3: Vec2,
|
||||
pub color: Color,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Rect {
|
||||
pub bl: Vec2,
|
||||
pub tr: Vec2,
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
pub fn from_xy_size(xy: Vec2, size: Vec2) -> Self {
|
||||
Self {
|
||||
bl: xy,
|
||||
tr: xy + size,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_circle_bounds(center: Vec2, radius: f32) -> Self {
|
||||
Self {
|
||||
bl: center - radius,
|
||||
tr: center + radius,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inset(&self, d: f32) -> Self {
|
||||
Self {
|
||||
bl: self.bl + d,
|
||||
tr: self.tr - d,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tl(&self) -> Vec2 {
|
||||
Vec2::new(self.bl.x, self.tr.y)
|
||||
}
|
||||
|
||||
pub fn br(&self) -> Vec2 {
|
||||
Vec2::new(self.tr.x, self.bl.y)
|
||||
}
|
||||
|
||||
pub fn offset(&self, offset: Vec2) -> Self {
|
||||
Self {
|
||||
bl: self.bl + offset,
|
||||
tr: self.tr + offset,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clip(&self, other: &Self) -> Self {
|
||||
Self {
|
||||
bl: self.bl.max(other.bl),
|
||||
tr: self.tr.min(other.tr),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_rect(&self, other: &Self) -> bool {
|
||||
self.bl.x < other.bl.x
|
||||
&& self.bl.y < other.bl.y
|
||||
&& self.tr.x > other.tr.x
|
||||
&& self.tr.y > other.tr.y
|
||||
}
|
||||
|
||||
pub fn contains_point(&self, xy: Vec2) -> bool {
|
||||
self.bl.x < xy.x && self.bl.y < xy.y && self.tr.x > xy.x && self.tr.y > xy.y
|
||||
}
|
||||
|
||||
pub fn width(&self) -> f32 {
|
||||
self.tr.x - self.bl.x
|
||||
}
|
||||
|
||||
pub fn height(&self) -> f32 {
|
||||
self.tr.y - self.bl.y
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrawContext {
|
||||
panel: UiPanel,
|
||||
offset: Vec2,
|
||||
offset: Option<Vec2>,
|
||||
clip_rect: Option<Rect>,
|
||||
opacity: Option<f32>,
|
||||
}
|
||||
|
||||
|
@ -18,15 +112,18 @@ impl DrawContext {
|
|||
pub fn new(panel: UiPanel) -> Self {
|
||||
Self {
|
||||
panel,
|
||||
offset: Vec2::ZERO,
|
||||
offset: None,
|
||||
clip_rect: None,
|
||||
opacity: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_triangle(&self, v1: Vec2, v2: Vec2, v3: Vec2, mut color: Color) {
|
||||
let v1 = v1 + self.offset;
|
||||
let v2 = v2 + self.offset;
|
||||
let v3 = v3 + self.offset;
|
||||
pub fn draw_triangle(&self, mut v1: Vec2, mut v2: Vec2, mut v3: Vec2, mut color: Color) {
|
||||
if let Some(offset) = self.offset {
|
||||
v1 += offset;
|
||||
v2 += offset;
|
||||
v3 += offset;
|
||||
}
|
||||
|
||||
if let Some(opacity) = self.opacity.as_ref() {
|
||||
color.a *= opacity;
|
||||
|
@ -97,56 +194,99 @@ impl DrawContext {
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
pub fn draw_rect(&self, rect: Rect, color: Color) {
|
||||
let v1 = rect.bl;
|
||||
let v2 = Vec2::new(rect.bl.x, rect.tr.y);
|
||||
let v3 = Vec2::new(rect.tr.x, rect.bl.y);
|
||||
let v4 = rect.tr;
|
||||
|
||||
self.draw_triangle(v1, v2, v3, color);
|
||||
self.draw_triangle(v2, v3, v4, color);
|
||||
}
|
||||
|
||||
pub fn draw_rounded_rect(&self, xy: Vec2, size: Vec2, radius: f32, color: Color) {
|
||||
let xoff = Vec2::new(radius, 0.0);
|
||||
let yoff = Vec2::new(0.0, radius);
|
||||
let sizex = Vec2::new(size.x, 0.0);
|
||||
let sizey = Vec2::new(0.0, size.y);
|
||||
let inner_size = size - Vec2::new(radius, radius) * 2.0;
|
||||
let lr_edge_size = Vec2::new(radius, inner_size.y);
|
||||
let tb_edge_size = Vec2::new(inner_size.x, radius);
|
||||
|
||||
self.draw_rect(xy + yoff, lr_edge_size, color); // left edge
|
||||
self.draw_rect(xy + yoff + sizex - xoff, lr_edge_size, color); // right edge
|
||||
self.draw_rect(xy + xoff, tb_edge_size, color); // bottom edge
|
||||
self.draw_rect(xy + xoff + sizey - yoff, tb_edge_size, color); // top edge
|
||||
|
||||
let tr = xy + size - xoff - yoff;
|
||||
let br = xy + sizex - xoff + yoff;
|
||||
let bl = xy + xoff + yoff;
|
||||
let tl = xy + sizey + xoff - yoff;
|
||||
|
||||
self.draw_quarter_circle(Corner::TopRight, tr, radius, color);
|
||||
self.draw_quarter_circle(Corner::BottomRight, br, radius, color);
|
||||
self.draw_quarter_circle(Corner::BottomLeft, bl, radius, color);
|
||||
self.draw_quarter_circle(Corner::TopLeft, tl, radius, color);
|
||||
|
||||
self.draw_rect(bl, inner_size, color);
|
||||
pub fn draw_rounded_rect(&self, rect: Rect, radius: f32, color: Color) {
|
||||
self.draw_partially_rounded_rect(CornerFlags::ALL, rect, radius, color);
|
||||
}
|
||||
|
||||
pub fn with_offset(&self, offset: Vec2) -> Self {
|
||||
pub fn draw_partially_rounded_rect(
|
||||
&self,
|
||||
corners: CornerFlags,
|
||||
rect: Rect,
|
||||
radius: f32,
|
||||
color: Color,
|
||||
) {
|
||||
if corners.is_empty() {
|
||||
self.draw_rect(rect, color);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut inner_rect = rect;
|
||||
let inset = rect.inset(radius);
|
||||
|
||||
if corners.intersects(CornerFlags::BOTTOM) {
|
||||
inner_rect.bl.y += radius;
|
||||
|
||||
let mut bottom_edge = Rect {
|
||||
bl: rect.bl,
|
||||
tr: Vec2::new(rect.tr.x, rect.bl.y + radius),
|
||||
};
|
||||
|
||||
if corners.contains(CornerFlags::BOTTOM_LEFT) {
|
||||
bottom_edge.bl.x += radius;
|
||||
self.draw_quarter_circle(Corner::BottomLeft, inset.bl, radius, color);
|
||||
}
|
||||
|
||||
if corners.contains(CornerFlags::BOTTOM_RIGHT) {
|
||||
bottom_edge.tr.x -= radius;
|
||||
self.draw_quarter_circle(Corner::BottomRight, inset.br(), radius, color);
|
||||
}
|
||||
|
||||
self.draw_rect(bottom_edge, color);
|
||||
}
|
||||
|
||||
if corners.intersects(CornerFlags::TOP) {
|
||||
inner_rect.tr.y -= radius;
|
||||
|
||||
let mut top_edge = Rect {
|
||||
bl: Vec2::new(rect.bl.x, rect.tr.y - radius),
|
||||
tr: rect.tr,
|
||||
};
|
||||
|
||||
if corners.contains(CornerFlags::TOP_LEFT) {
|
||||
top_edge.bl.x += radius;
|
||||
self.draw_quarter_circle(Corner::TopLeft, inset.tl(), radius, color);
|
||||
}
|
||||
|
||||
if corners.contains(CornerFlags::TOP_RIGHT) {
|
||||
top_edge.tr.x -= radius;
|
||||
self.draw_quarter_circle(Corner::TopRight, inset.tr, radius, color);
|
||||
}
|
||||
|
||||
self.draw_rect(top_edge, color);
|
||||
}
|
||||
|
||||
self.draw_rect(inner_rect, color);
|
||||
}
|
||||
|
||||
pub fn get_clip_rect(&self) -> &Option<Rect> {
|
||||
&self.clip_rect
|
||||
}
|
||||
|
||||
pub fn with_offset(&self, mut offset: Vec2) -> Self {
|
||||
if let Some(old) = self.offset {
|
||||
offset += old;
|
||||
}
|
||||
|
||||
Self {
|
||||
offset: self.offset + offset,
|
||||
offset: Some(offset),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_opacity(&self, opacity: f32) -> Self {
|
||||
let opacity = if let Some(old) = self.opacity.as_ref() {
|
||||
opacity * old
|
||||
} else {
|
||||
opacity
|
||||
};
|
||||
pub fn with_opacity(&self, mut opacity: f32) -> Self {
|
||||
if let Some(old) = self.opacity {
|
||||
opacity *= old;
|
||||
}
|
||||
|
||||
Self {
|
||||
opacity: Some(opacity),
|
||||
|
|
296
src/widgets.rs
296
src/widgets.rs
|
@ -1,5 +1,5 @@
|
|||
use crate::anim::Animation;
|
||||
use crate::draw::{Corner, DrawContext};
|
||||
use crate::draw::{Corner, CornerFlags, DrawContext, Rect};
|
||||
use crate::{Color, CursorEventKind, Vec2};
|
||||
use keyframe::functions::*;
|
||||
|
||||
|
@ -91,8 +91,9 @@ impl Widget for RoundButton {
|
|||
}
|
||||
|
||||
pub struct RectButton {
|
||||
pub pos: Vec2,
|
||||
pub size: Vec2,
|
||||
pub rect: Rect,
|
||||
pub rounded_corners: CornerFlags,
|
||||
pub radius: f32,
|
||||
pub was_clicked: bool,
|
||||
pub is_selected: bool,
|
||||
pub is_hovering: bool,
|
||||
|
@ -110,10 +111,11 @@ impl RectButton {
|
|||
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 {
|
||||
pub fn new(rect: Rect, rounded_corners: CornerFlags, radius: f32) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
size,
|
||||
rect,
|
||||
rounded_corners,
|
||||
radius,
|
||||
was_clicked: false,
|
||||
is_selected: false,
|
||||
is_hovering: false,
|
||||
|
@ -134,12 +136,16 @@ impl Widget for RectButton {
|
|||
}
|
||||
|
||||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
ctx.draw_rect(self.pos, self.size, self.color_anim.get());
|
||||
ctx.draw_partially_rounded_rect(
|
||||
self.rounded_corners,
|
||||
self.rect,
|
||||
self.radius,
|
||||
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;
|
||||
let is_on = self.rect.contains_point(at);
|
||||
|
||||
match kind {
|
||||
CursorEventKind::Hover | CursorEventKind::Drag => {
|
||||
|
@ -167,7 +173,158 @@ impl Widget for RectButton {
|
|||
if self.is_selected {
|
||||
self.was_clicked = true;
|
||||
self.is_selected = false;
|
||||
self.color_anim.ease_to(Self::HOVER_COLOR);
|
||||
|
||||
if self.is_hovering {
|
||||
self.color_anim.ease_to(Self::HOVER_COLOR);
|
||||
} else {
|
||||
self.color_anim.ease_to(Self::INACTIVE_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ScrollBarStyle {
|
||||
pub margin: Vec2,
|
||||
pub body_radius: f32,
|
||||
pub body_width: f32,
|
||||
pub body_idle_color: Color,
|
||||
pub body_hover_color: Color,
|
||||
pub body_selected_color: Color,
|
||||
pub rail_width: f32,
|
||||
pub rail_color: Color,
|
||||
}
|
||||
|
||||
impl Default for ScrollBarStyle {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
margin: Vec2::new(0.01, 0.01),
|
||||
body_radius: 0.005,
|
||||
body_width: 0.015,
|
||||
body_idle_color: Color::new(0.5, 0.5, 0.5, 1.0),
|
||||
body_hover_color: Color::new(0.8, 0.8, 0.8, 1.0),
|
||||
body_selected_color: Color::new(1.0, 1.0, 0.0, 1.0),
|
||||
rail_width: 0.005,
|
||||
rail_color: Color::new(0.7, 0.7, 0.7, 0.5),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ScrollBar {
|
||||
height: f32,
|
||||
style: ScrollBarStyle,
|
||||
scroll: f32,
|
||||
content_height: f32,
|
||||
rail_rect: Rect,
|
||||
body_color_anim: Animation<EaseInQuad, Color>,
|
||||
is_hovering: bool,
|
||||
is_selected: bool,
|
||||
grab_coord: f32,
|
||||
grab_scroll: f32,
|
||||
}
|
||||
|
||||
impl ScrollBar {
|
||||
pub fn new(height: f32, content_height: f32, style: ScrollBarStyle) -> Self {
|
||||
let center_x = style.body_width / 2.0 + style.margin.x;
|
||||
let rail_rect = Rect {
|
||||
bl: Vec2::new(center_x - style.rail_width / 2.0, style.margin.y),
|
||||
tr: Vec2::new(center_x + style.rail_width / 2.0, height - style.margin.y),
|
||||
};
|
||||
|
||||
let body_color_anim = Animation::new(
|
||||
EaseInQuad,
|
||||
0.05,
|
||||
style.body_idle_color,
|
||||
style.body_idle_color,
|
||||
);
|
||||
|
||||
Self {
|
||||
height,
|
||||
style,
|
||||
scroll: 0.0,
|
||||
content_height,
|
||||
rail_rect,
|
||||
body_color_anim,
|
||||
is_hovering: false,
|
||||
is_selected: false,
|
||||
grab_coord: 0.0,
|
||||
grab_scroll: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn body_rect(&self) -> Rect {
|
||||
let style = &self.style;
|
||||
let rail_height = self.rail_rect.height();
|
||||
let body_height = (self.height / self.content_height) * rail_height;
|
||||
let body_y = rail_height - (self.scroll / self.content_height) * rail_height - body_height;
|
||||
let body_xy = Vec2::new(style.margin.x, body_y + style.margin.y);
|
||||
let body_size = Vec2::new(style.body_width, body_height);
|
||||
Rect::from_xy_size(body_xy, body_size)
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for ScrollBar {
|
||||
fn update(&mut self, dt: f32) {
|
||||
self.body_color_anim.update(dt);
|
||||
}
|
||||
|
||||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
let style = &self.style;
|
||||
let body_rect = self.body_rect();
|
||||
ctx.draw_rect(self.rail_rect, style.rail_color);
|
||||
ctx.draw_rounded_rect(body_rect, style.body_radius, self.body_color_anim.get());
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||
let is_on = self.body_rect().contains_point(at);
|
||||
|
||||
match kind {
|
||||
CursorEventKind::Hover | CursorEventKind::Drag => {
|
||||
if is_on {
|
||||
if !self.is_hovering && !self.is_selected {
|
||||
self.body_color_anim.ease_to(self.style.body_hover_color);
|
||||
}
|
||||
|
||||
self.is_hovering = true;
|
||||
} else {
|
||||
if self.is_hovering && !self.is_selected {
|
||||
self.body_color_anim.ease_to(self.style.body_idle_color);
|
||||
}
|
||||
|
||||
self.is_hovering = false;
|
||||
}
|
||||
|
||||
if kind == CursorEventKind::Drag && self.is_selected {
|
||||
self.scroll = ((self.grab_coord - at.y) / self.rail_rect.height())
|
||||
* self.content_height
|
||||
+ self.grab_scroll;
|
||||
|
||||
let scroll_cap = self.content_height - self.height;
|
||||
if self.scroll > scroll_cap {
|
||||
self.scroll = scroll_cap;
|
||||
} else if self.scroll < 0.0 {
|
||||
self.scroll = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
CursorEventKind::Select => {
|
||||
if is_on {
|
||||
self.is_selected = true;
|
||||
self.body_color_anim.ease_to(self.style.body_selected_color);
|
||||
self.grab_coord = at.y;
|
||||
self.grab_scroll = self.scroll;
|
||||
}
|
||||
}
|
||||
CursorEventKind::Deselect => {
|
||||
if self.is_selected {
|
||||
self.is_selected = false;
|
||||
|
||||
if self.is_hovering {
|
||||
self.body_color_anim.ease_to(self.style.body_hover_color);
|
||||
} else {
|
||||
self.body_color_anim.ease_to(self.style.body_idle_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -409,7 +566,7 @@ impl<T: Widget> Widget for Offset<T> {
|
|||
pub struct Reveal<T> {
|
||||
inner: T,
|
||||
slide_anim: Animation<EaseIn>,
|
||||
opacity_anim: Animation<EaseOut>,
|
||||
opacity_anim: Animation<Linear>,
|
||||
state: bool,
|
||||
}
|
||||
|
||||
|
@ -418,7 +575,7 @@ impl<T: Widget> Reveal<T> {
|
|||
Self {
|
||||
inner,
|
||||
slide_anim: Animation::new(EaseIn, duration, slide, 0.0),
|
||||
opacity_anim: Animation::new(EaseOut, duration, 0.0, 1.0),
|
||||
opacity_anim: Animation::new(Linear, duration, 0.0, 1.0),
|
||||
state: false,
|
||||
}
|
||||
}
|
||||
|
@ -472,7 +629,7 @@ impl<T: Widget> Widget for Reveal<T> {
|
|||
|
||||
pub struct MainMenu {
|
||||
pub menu: ScrollMenu<RoundButton>,
|
||||
pub inventory: Reveal<Offset<Inventory>>,
|
||||
pub inventory: Reveal<Offset<TabMenu>>,
|
||||
}
|
||||
|
||||
impl MainMenu {
|
||||
|
@ -487,7 +644,7 @@ impl Default for MainMenu {
|
|||
buttons.push(button);
|
||||
}
|
||||
|
||||
let inventory = Inventory::new();
|
||||
let inventory = TabMenu::new();
|
||||
let inventory = Offset::new(inventory, Vec2::new(Self::SUBMENU_SPACING, 0.0));
|
||||
let inventory = Reveal::new(inventory, -0.02, 0.1);
|
||||
|
||||
|
@ -527,21 +684,22 @@ impl Widget for MainMenu {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Inventory {
|
||||
pub struct TabMenu {
|
||||
tabs: Vec<RectButton>,
|
||||
scroll_bar: Offset<ScrollBar>,
|
||||
head_rect: Rect,
|
||||
separator_rect: Rect,
|
||||
}
|
||||
|
||||
impl Inventory {
|
||||
impl TabMenu {
|
||||
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;
|
||||
const INNER_RADIUS: f32 = 0.005;
|
||||
const CONTENT_WIDTH: f32 = 0.4;
|
||||
|
||||
pub fn new() -> Self {
|
||||
let tab_size = Vec2::new(Self::TAB_WIDTH, Self::TAB_HEIGHT);
|
||||
|
@ -550,18 +708,56 @@ impl Inventory {
|
|||
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));
|
||||
let radius = Self::HEAD_RADIUS;
|
||||
|
||||
let corners = if i == Self::TAB_NUM - 1 {
|
||||
CornerFlags::BOTTOM_LEFT
|
||||
} else {
|
||||
CornerFlags::empty()
|
||||
};
|
||||
|
||||
let rect = Rect::from_xy_size(pos, tab_size);
|
||||
tabs.push(RectButton::new(rect, corners, radius));
|
||||
}
|
||||
|
||||
Self { tabs }
|
||||
let tab_list_height = Self::TAB_NUM as f32 * Self::TAB_HEIGHT;
|
||||
|
||||
let scroll_bar = ScrollBar::new(tab_list_height, tab_list_height * 3.0, Default::default());
|
||||
let scroll_x = Self::TAB_WIDTH + Self::SEPARATOR_WIDTH + Self::CONTENT_WIDTH;
|
||||
let scroll_bar = Offset::new(scroll_bar, Vec2::new(scroll_x, -tab_list_height));
|
||||
|
||||
let separator_rect = Rect {
|
||||
bl: Vec2::new(Self::TAB_WIDTH, -tab_list_height),
|
||||
tr: Vec2::new(Self::TAB_WIDTH + Self::SEPARATOR_WIDTH, 0.0),
|
||||
};
|
||||
|
||||
let head_width = Self::TAB_WIDTH
|
||||
+ Self::SEPARATOR_WIDTH
|
||||
+ Self::CONTENT_WIDTH
|
||||
+ scroll_bar.inner.style.body_width
|
||||
+ scroll_bar.inner.style.margin.x * 2.0;
|
||||
|
||||
let head_rect = Rect {
|
||||
bl: Vec2::ZERO,
|
||||
tr: Vec2::new(head_width, Self::HEAD_HEIGHT),
|
||||
};
|
||||
|
||||
Self {
|
||||
tabs,
|
||||
scroll_bar,
|
||||
separator_rect,
|
||||
head_rect,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Inventory {
|
||||
impl Widget for TabMenu {
|
||||
fn update(&mut self, dt: f32) {
|
||||
for tab in self.tabs.iter_mut() {
|
||||
tab.update(dt);
|
||||
}
|
||||
|
||||
self.scroll_bar.update(dt);
|
||||
}
|
||||
|
||||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
|
@ -576,59 +772,29 @@ impl Widget for Inventory {
|
|||
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,
|
||||
ctx.draw_partially_rounded_rect(
|
||||
CornerFlags::BOTTOM_RIGHT,
|
||||
self.separator_rect,
|
||||
Self::INNER_RADIUS,
|
||||
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,
|
||||
ctx.draw_partially_rounded_rect(
|
||||
CornerFlags::TOP_LEFT | CornerFlags::TOP_RIGHT,
|
||||
self.head_rect,
|
||||
Self::HEAD_RADIUS,
|
||||
head_color,
|
||||
);
|
||||
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);
|
||||
}
|
||||
}
|
||||
self.scroll_bar.draw(ctx);
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||
for tab in self.tabs.iter_mut() {
|
||||
tab.on_cursor_event(kind, at);
|
||||
}
|
||||
|
||||
self.scroll_bar.on_cursor_event(kind, at);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue