Rect clipping + inventory squares

This commit is contained in:
mars 2022-07-14 19:09:18 -06:00
parent 4e01c3937d
commit 19c107a106
3 changed files with 213 additions and 17 deletions

View File

@ -52,6 +52,13 @@ 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 {
Self {
bl: self.bl + d,
@ -74,10 +81,24 @@ impl Rect {
}
}
pub fn clip(&self, other: &Self) -> Self {
Self {
pub fn is_valid(&self) -> bool {
self.bl.cmplt(self.tr).all()
}
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),
tr: self.tr.min(other.tr),
};
if clipped.is_valid() {
Some(clipped)
} else {
None
}
}
@ -118,7 +139,20 @@ impl DrawContext {
}
}
pub fn draw_triangle(&self, mut v1: Vec2, mut v2: Vec2, mut v3: Vec2, mut color: Color) {
pub fn draw_triangle(&self, v1: Vec2, v2: Vec2, v3: Vec2, 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 {
v1 += offset;
v2 += offset;
@ -195,13 +229,23 @@ impl DrawContext {
}
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 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);
self.draw_triangle_noclip(v1, v2, v3, color);
self.draw_triangle_noclip(v2, v3, v4, color);
}
pub fn draw_rounded_rect(&self, rect: Rect, radius: f32, color: Color) {
@ -272,17 +316,29 @@ impl DrawContext {
&self.clip_rect
}
pub fn with_offset(&self, mut offset: Vec2) -> Self {
if let Some(old) = self.offset {
offset += old;
}
pub fn with_offset(&self, offset: Vec2) -> Self {
Self {
offset: Some(offset),
offset: self.offset.map(|old| old + offset).or(Some(offset)),
clip_rect: self.clip_rect.map(|r| r.offset(-offset)),
..*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 {
if let Some(old) = self.opacity {
opacity *= old;

View File

@ -52,6 +52,12 @@ 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 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 {
Self { r, g, b, a }

View File

@ -1,5 +1,5 @@
use crate::anim::Animation;
use crate::draw::{Corner, CornerFlags, DrawContext, Rect};
use crate::draw::{CornerFlags, DrawContext, Rect};
use crate::{Color, CursorEventKind, Vec2};
use keyframe::functions::*;
@ -13,6 +13,10 @@ pub trait Button {
fn was_clicked(&self) -> bool;
}
pub trait FixedWidth {
fn get_width(&self) -> f32;
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ButtonState {
Idle,
@ -214,6 +218,7 @@ impl Default for ScrollBarStyle {
pub struct ScrollBar {
height: f32,
style: ScrollBarStyle,
is_dirty: bool,
scroll: f32,
content_height: f32,
rail_rect: Rect,
@ -242,6 +247,7 @@ impl ScrollBar {
Self {
height,
style,
is_dirty: true,
scroll: 0.0,
content_height,
rail_rect,
@ -263,6 +269,10 @@ impl ScrollBar {
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
}
@ -270,6 +280,7 @@ impl ScrollBar {
impl Widget for ScrollBar {
fn update(&mut self, dt: f32) {
self.is_dirty = false;
self.body_color_anim.update(dt);
}
@ -310,6 +321,8 @@ impl Widget for ScrollBar {
} else if self.scroll < 0.0 {
self.scroll = 0.0;
}
self.is_dirty = true;
}
}
CursorEventKind::Select => {
@ -335,6 +348,66 @@ 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)]
pub enum ScrollMenuState {
Opening,
@ -549,6 +622,10 @@ impl<T: Widget> Offset<T> {
pub fn new(inner: T, offset: Vec2) -> Self {
Self { inner, offset }
}
pub fn set_offset(&mut self, offset: Vec2) {
self.offset = offset;
}
}
impl<T: Widget> Widget for Offset<T> {
@ -690,7 +767,7 @@ impl Widget for MainMenu {
pub struct TabMenu {
tabs: Vec<RectButton>,
scroll_bar: Offset<ScrollBar>,
view: Offset<ScrollView<Inventory>>,
head_rect: Rect,
separator_rect: Rect,
}
@ -746,9 +823,23 @@ impl TabMenu {
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 {
tabs,
scroll_bar,
view,
separator_rect,
head_rect,
}
@ -761,7 +852,7 @@ impl Widget for TabMenu {
tab.update(dt);
}
self.scroll_bar.update(dt);
self.view.update(dt);
}
fn draw(&mut self, ctx: &DrawContext) {
@ -791,7 +882,7 @@ impl Widget for TabMenu {
head_color,
);
self.scroll_bar.draw(ctx);
self.view.draw(ctx);
}
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
@ -799,6 +890,49 @@ impl Widget for TabMenu {
tab.on_cursor_event(kind, at);
}
self.scroll_bar.on_cursor_event(kind, at);
self.view.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) {}
}