Compare commits
No commits in common. "19c107a106f554e843854685abc56cf71c522a97" and "9d83792ef7db0db9859744e78da4ccdb34246a9e" have entirely different histories.
19c107a106
...
9d83792ef7
78
src/draw.rs
78
src/draw.rs
|
@ -52,13 +52,6 @@ impl Rect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_triangle_bounds(tri: &ColoredTriangle) -> Self {
|
|
||||||
Self {
|
|
||||||
bl: tri.v1.min(tri.v2).min(tri.v3),
|
|
||||||
tr: tri.v1.max(tri.v2).max(tri.v3),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inset(&self, d: f32) -> Self {
|
pub fn inset(&self, d: f32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bl: self.bl + d,
|
bl: self.bl + d,
|
||||||
|
@ -81,24 +74,10 @@ impl Rect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid(&self) -> bool {
|
pub fn clip(&self, other: &Self) -> Self {
|
||||||
self.bl.cmplt(self.tr).all()
|
Self {
|
||||||
}
|
|
||||||
|
|
||||||
pub fn intersects_rect(&self, other: &Self) -> bool {
|
|
||||||
self.bl.cmple(other.tr).all() && self.tr.cmpge(other.bl).all()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn intersection(&self, other: &Self) -> Option<Self> {
|
|
||||||
let clipped = Self {
|
|
||||||
bl: self.bl.max(other.bl),
|
bl: self.bl.max(other.bl),
|
||||||
tr: self.tr.min(other.tr),
|
tr: self.tr.min(other.tr),
|
||||||
};
|
|
||||||
|
|
||||||
if clipped.is_valid() {
|
|
||||||
Some(clipped)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,20 +118,7 @@ impl DrawContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_triangle(&self, v1: Vec2, v2: Vec2, v3: Vec2, color: Color) {
|
pub fn draw_triangle(&self, mut v1: Vec2, mut v2: Vec2, mut v3: Vec2, mut color: Color) {
|
||||||
if let Some(clip_rect) = self.clip_rect.as_ref() {
|
|
||||||
let tri = ColoredTriangle { v1, v2, v3, color };
|
|
||||||
let bb = Rect::from_triangle_bounds(&tri);
|
|
||||||
|
|
||||||
if clip_rect.contains_rect(&bb) {
|
|
||||||
self.draw_triangle_noclip(tri.v1, tri.v2, tri.v3, tri.color);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.draw_triangle_noclip(v1, v2, v3, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_triangle_noclip(&self, mut v1: Vec2, mut v2: Vec2, mut v3: Vec2, mut color: Color) {
|
|
||||||
if let Some(offset) = self.offset {
|
if let Some(offset) = self.offset {
|
||||||
v1 += offset;
|
v1 += offset;
|
||||||
v2 += offset;
|
v2 += offset;
|
||||||
|
@ -229,23 +195,13 @@ impl DrawContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_rect(&self, rect: Rect, color: Color) {
|
pub fn draw_rect(&self, rect: Rect, color: Color) {
|
||||||
let rect = if let Some(clip_rect) = self.clip_rect.as_ref() {
|
|
||||||
if let Some(clipped) = clip_rect.intersection(&rect) {
|
|
||||||
clipped
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rect
|
|
||||||
};
|
|
||||||
|
|
||||||
let v1 = rect.bl;
|
let v1 = rect.bl;
|
||||||
let v2 = Vec2::new(rect.bl.x, rect.tr.y);
|
let v2 = Vec2::new(rect.bl.x, rect.tr.y);
|
||||||
let v3 = Vec2::new(rect.tr.x, rect.bl.y);
|
let v3 = Vec2::new(rect.tr.x, rect.bl.y);
|
||||||
let v4 = rect.tr;
|
let v4 = rect.tr;
|
||||||
|
|
||||||
self.draw_triangle_noclip(v1, v2, v3, color);
|
self.draw_triangle(v1, v2, v3, color);
|
||||||
self.draw_triangle_noclip(v2, v3, v4, color);
|
self.draw_triangle(v2, v3, v4, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_rounded_rect(&self, rect: Rect, radius: f32, color: Color) {
|
pub fn draw_rounded_rect(&self, rect: Rect, radius: f32, color: Color) {
|
||||||
|
@ -316,29 +272,17 @@ impl DrawContext {
|
||||||
&self.clip_rect
|
&self.clip_rect
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_offset(&self, offset: Vec2) -> Self {
|
pub fn with_offset(&self, mut offset: Vec2) -> Self {
|
||||||
|
if let Some(old) = self.offset {
|
||||||
|
offset += old;
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
offset: self.offset.map(|old| old + offset).or(Some(offset)),
|
offset: Some(offset),
|
||||||
clip_rect: self.clip_rect.map(|r| r.offset(-offset)),
|
|
||||||
..*self
|
..*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_clip_rect(&self, mut clip_rect: Rect) -> Option<Self> {
|
|
||||||
if let Some(old) = self.clip_rect {
|
|
||||||
if let Some(clipped) = old.intersection(&clip_rect) {
|
|
||||||
clip_rect = clipped;
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Self {
|
|
||||||
clip_rect: Some(clip_rect),
|
|
||||||
..*self
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_opacity(&self, mut opacity: f32) -> Self {
|
pub fn with_opacity(&self, mut opacity: f32) -> Self {
|
||||||
if let Some(old) = self.opacity {
|
if let Some(old) = self.opacity {
|
||||||
opacity *= old;
|
opacity *= old;
|
||||||
|
|
|
@ -52,12 +52,6 @@ impl Color {
|
||||||
pub const WHITE: Self = Self::new(1., 1., 1., 1.);
|
pub const WHITE: Self = Self::new(1., 1., 1., 1.);
|
||||||
pub const BLACK: Self = Self::new(0., 0., 0., 1.);
|
pub const BLACK: Self = Self::new(0., 0., 0., 1.);
|
||||||
pub const TRANSPARENT: Self = Self::new(0., 0., 0., 0.);
|
pub const TRANSPARENT: Self = Self::new(0., 0., 0., 0.);
|
||||||
pub const RED: Self = Self::new(1., 0., 0., 1.);
|
|
||||||
pub const GREEN: Self = Self::new(1., 0., 0., 1.);
|
|
||||||
pub const BLUE: Self = Self::new(0., 1., 0., 1.);
|
|
||||||
pub const YELLOW: Self = Self::new(1., 1., 0., 1.);
|
|
||||||
pub const MAGENTA: Self = Self::new(1., 0., 1., 1.);
|
|
||||||
pub const CYAN: Self = Self::new(0., 1., 1., 1.);
|
|
||||||
|
|
||||||
pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
|
pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||||
Self { r, g, b, a }
|
Self { r, g, b, a }
|
||||||
|
|
156
src/widgets.rs
156
src/widgets.rs
|
@ -1,5 +1,5 @@
|
||||||
use crate::anim::Animation;
|
use crate::anim::Animation;
|
||||||
use crate::draw::{CornerFlags, DrawContext, Rect};
|
use crate::draw::{Corner, CornerFlags, DrawContext, Rect};
|
||||||
use crate::{Color, CursorEventKind, Vec2};
|
use crate::{Color, CursorEventKind, Vec2};
|
||||||
use keyframe::functions::*;
|
use keyframe::functions::*;
|
||||||
|
|
||||||
|
@ -13,10 +13,6 @@ pub trait Button {
|
||||||
fn was_clicked(&self) -> bool;
|
fn was_clicked(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FixedWidth {
|
|
||||||
fn get_width(&self) -> f32;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum ButtonState {
|
pub enum ButtonState {
|
||||||
Idle,
|
Idle,
|
||||||
|
@ -218,7 +214,6 @@ impl Default for ScrollBarStyle {
|
||||||
pub struct ScrollBar {
|
pub struct ScrollBar {
|
||||||
height: f32,
|
height: f32,
|
||||||
style: ScrollBarStyle,
|
style: ScrollBarStyle,
|
||||||
is_dirty: bool,
|
|
||||||
scroll: f32,
|
scroll: f32,
|
||||||
content_height: f32,
|
content_height: f32,
|
||||||
rail_rect: Rect,
|
rail_rect: Rect,
|
||||||
|
@ -247,7 +242,6 @@ impl ScrollBar {
|
||||||
Self {
|
Self {
|
||||||
height,
|
height,
|
||||||
style,
|
style,
|
||||||
is_dirty: true,
|
|
||||||
scroll: 0.0,
|
scroll: 0.0,
|
||||||
content_height,
|
content_height,
|
||||||
rail_rect,
|
rail_rect,
|
||||||
|
@ -259,7 +253,7 @@ impl ScrollBar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_body_rect(&self) -> Rect {
|
pub fn body_rect(&self) -> Rect {
|
||||||
let style = &self.style;
|
let style = &self.style;
|
||||||
let rail_height = self.rail_rect.height();
|
let rail_height = self.rail_rect.height();
|
||||||
let body_height = (self.height / self.content_height) * rail_height;
|
let body_height = (self.height / self.content_height) * rail_height;
|
||||||
|
@ -268,31 +262,22 @@ impl ScrollBar {
|
||||||
let body_size = Vec2::new(style.body_width, body_height);
|
let body_size = Vec2::new(style.body_width, body_height);
|
||||||
Rect::from_xy_size(body_xy, body_size)
|
Rect::from_xy_size(body_xy, body_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_dirty(&self) -> bool {
|
|
||||||
self.is_dirty
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_scroll(&self) -> f32 {
|
|
||||||
self.scroll
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for ScrollBar {
|
impl Widget for ScrollBar {
|
||||||
fn update(&mut self, dt: f32) {
|
fn update(&mut self, dt: f32) {
|
||||||
self.is_dirty = false;
|
|
||||||
self.body_color_anim.update(dt);
|
self.body_color_anim.update(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, ctx: &DrawContext) {
|
fn draw(&mut self, ctx: &DrawContext) {
|
||||||
let style = &self.style;
|
let style = &self.style;
|
||||||
let body_rect = self.get_body_rect();
|
let body_rect = self.body_rect();
|
||||||
ctx.draw_rect(self.rail_rect, style.rail_color);
|
ctx.draw_rect(self.rail_rect, style.rail_color);
|
||||||
ctx.draw_rounded_rect(body_rect, style.body_radius, self.body_color_anim.get());
|
ctx.draw_rounded_rect(body_rect, style.body_radius, self.body_color_anim.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||||
let is_on = self.get_body_rect().contains_point(at);
|
let is_on = self.body_rect().contains_point(at);
|
||||||
|
|
||||||
match kind {
|
match kind {
|
||||||
CursorEventKind::Hover | CursorEventKind::Drag => {
|
CursorEventKind::Hover | CursorEventKind::Drag => {
|
||||||
|
@ -321,8 +306,6 @@ impl Widget for ScrollBar {
|
||||||
} else if self.scroll < 0.0 {
|
} else if self.scroll < 0.0 {
|
||||||
self.scroll = 0.0;
|
self.scroll = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.is_dirty = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CursorEventKind::Select => {
|
CursorEventKind::Select => {
|
||||||
|
@ -348,66 +331,6 @@ impl Widget for ScrollBar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ScrollView<T> {
|
|
||||||
inner: Offset<T>,
|
|
||||||
scroll_bar: Offset<ScrollBar>,
|
|
||||||
clip_rect: Rect,
|
|
||||||
height: f32,
|
|
||||||
content_height: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Widget> ScrollView<T> {
|
|
||||||
pub fn new(
|
|
||||||
bar_style: ScrollBarStyle,
|
|
||||||
width: f32,
|
|
||||||
height: f32,
|
|
||||||
inner_cb: impl FnOnce(f32) -> (T, f32),
|
|
||||||
) -> Self {
|
|
||||||
let content_width = width - bar_style.body_width - bar_style.margin.x * 2.0;
|
|
||||||
let (inner, content_height) = inner_cb(content_width);
|
|
||||||
let inner = Offset::new(inner, Vec2::ZERO);
|
|
||||||
|
|
||||||
let scroll_bar_offset = Vec2::new(content_width, 0.0);
|
|
||||||
let scroll_bar = ScrollBar::new(height, content_height, bar_style);
|
|
||||||
let scroll_bar = Offset::new(scroll_bar, scroll_bar_offset);
|
|
||||||
|
|
||||||
let clip_rect = Rect::from_xy_size(Vec2::ZERO, Vec2::new(content_width, height));
|
|
||||||
|
|
||||||
Self {
|
|
||||||
inner,
|
|
||||||
scroll_bar,
|
|
||||||
clip_rect,
|
|
||||||
content_height,
|
|
||||||
height,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Widget> Widget for ScrollView<T> {
|
|
||||||
fn update(&mut self, dt: f32) {
|
|
||||||
if self.scroll_bar.inner.is_dirty() {
|
|
||||||
let yoff = self.scroll_bar.inner.get_scroll() - self.content_height + self.height;
|
|
||||||
self.inner.set_offset(Vec2::new(0.0, yoff));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.inner.update(dt);
|
|
||||||
self.scroll_bar.update(dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(&mut self, ctx: &DrawContext) {
|
|
||||||
self.scroll_bar.draw(ctx);
|
|
||||||
|
|
||||||
if let Some(ctx) = ctx.with_clip_rect(self.clip_rect) {
|
|
||||||
self.inner.draw(&ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
|
||||||
self.inner.on_cursor_event(kind, at);
|
|
||||||
self.scroll_bar.on_cursor_event(kind, at);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Eq, PartialEq)]
|
#[derive(Eq, PartialEq)]
|
||||||
pub enum ScrollMenuState {
|
pub enum ScrollMenuState {
|
||||||
Opening,
|
Opening,
|
||||||
|
@ -622,10 +545,6 @@ impl<T: Widget> Offset<T> {
|
||||||
pub fn new(inner: T, offset: Vec2) -> Self {
|
pub fn new(inner: T, offset: Vec2) -> Self {
|
||||||
Self { inner, offset }
|
Self { inner, offset }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_offset(&mut self, offset: Vec2) {
|
|
||||||
self.offset = offset;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Widget> Widget for Offset<T> {
|
impl<T: Widget> Widget for Offset<T> {
|
||||||
|
@ -767,7 +686,7 @@ impl Widget for MainMenu {
|
||||||
|
|
||||||
pub struct TabMenu {
|
pub struct TabMenu {
|
||||||
tabs: Vec<RectButton>,
|
tabs: Vec<RectButton>,
|
||||||
view: Offset<ScrollView<Inventory>>,
|
scroll_bar: Offset<ScrollBar>,
|
||||||
head_rect: Rect,
|
head_rect: Rect,
|
||||||
separator_rect: Rect,
|
separator_rect: Rect,
|
||||||
}
|
}
|
||||||
|
@ -823,23 +742,9 @@ impl TabMenu {
|
||||||
tr: Vec2::new(head_width, Self::HEAD_HEIGHT),
|
tr: Vec2::new(head_width, Self::HEAD_HEIGHT),
|
||||||
};
|
};
|
||||||
|
|
||||||
let view_rect = Rect {
|
|
||||||
bl: Vec2::new(Self::TAB_WIDTH + Self::SEPARATOR_WIDTH, -tab_list_height),
|
|
||||||
tr: Vec2::new(head_rect.tr.x, 0.0),
|
|
||||||
};
|
|
||||||
|
|
||||||
let view = ScrollView::new(
|
|
||||||
Default::default(),
|
|
||||||
view_rect.width(),
|
|
||||||
view_rect.height(),
|
|
||||||
|available_width: f32| Inventory::new(available_width),
|
|
||||||
);
|
|
||||||
|
|
||||||
let view = Offset::new(view, view_rect.bl);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
tabs,
|
tabs,
|
||||||
view,
|
scroll_bar,
|
||||||
separator_rect,
|
separator_rect,
|
||||||
head_rect,
|
head_rect,
|
||||||
}
|
}
|
||||||
|
@ -852,7 +757,7 @@ impl Widget for TabMenu {
|
||||||
tab.update(dt);
|
tab.update(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.view.update(dt);
|
self.scroll_bar.update(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, ctx: &DrawContext) {
|
fn draw(&mut self, ctx: &DrawContext) {
|
||||||
|
@ -882,7 +787,7 @@ impl Widget for TabMenu {
|
||||||
head_color,
|
head_color,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.view.draw(ctx);
|
self.scroll_bar.draw(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||||
|
@ -890,49 +795,6 @@ impl Widget for TabMenu {
|
||||||
tab.on_cursor_event(kind, at);
|
tab.on_cursor_event(kind, at);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.view.on_cursor_event(kind, at);
|
self.scroll_bar.on_cursor_event(kind, at);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Inventory {
|
|
||||||
width: f32,
|
|
||||||
height: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Inventory {
|
|
||||||
pub fn new(available_width: f32) -> (Self, f32) {
|
|
||||||
let height = 1.5;
|
|
||||||
|
|
||||||
(
|
|
||||||
Self {
|
|
||||||
width: available_width,
|
|
||||||
height,
|
|
||||||
},
|
|
||||||
height,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Widget for Inventory {
|
|
||||||
fn update(&mut self, dt: f32) {}
|
|
||||||
|
|
||||||
fn draw(&mut self, ctx: &DrawContext) {
|
|
||||||
let box_size = 0.04;
|
|
||||||
let box_margin = 0.01;
|
|
||||||
let box_stride = box_size + box_margin;
|
|
||||||
|
|
||||||
let grid_width = (self.width / box_stride).floor() as usize;
|
|
||||||
let grid_height = (self.height / box_stride).floor() as usize;
|
|
||||||
|
|
||||||
for x in 0..grid_width {
|
|
||||||
for y in 0..grid_height {
|
|
||||||
let off = Vec2::new(x as f32, y as f32) * box_stride;
|
|
||||||
let rect = Rect::from_xy_size(off, Vec2::new(box_size, box_size));
|
|
||||||
let color = Color::MAGENTA;
|
|
||||||
ctx.draw_rect(rect, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue