From f7c2a24572055cee8f3fb15b3167c84332f164ac Mon Sep 17 00:00:00 2001 From: mars Date: Tue, 12 Jul 2022 16:16:52 -0600 Subject: [PATCH] Add CornerFlags and draw_partially_rounded_rect() --- Cargo.toml | 3 +- src/draw.rs | 96 +++++++++++++++++++++++++++++++++++++++++++------- src/widgets.rs | 26 +++++++++++--- 3 files changed, 107 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c850a1f..24af3d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/draw.rs b/src/draw.rs index 9690c1c..3e3bea4 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -1,5 +1,6 @@ use crate::abi::UiPanel; use crate::{Color, Vec2}; +use bitflags::bitflags; pub enum Corner { TopRight, @@ -8,6 +9,20 @@ 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; + } +} + pub struct DrawContext { panel: UiPanel, offset: Vec2, @@ -108,30 +123,85 @@ impl DrawContext { } pub fn draw_rounded_rect(&self, xy: Vec2, size: Vec2, radius: f32, color: Color) { + self.draw_partially_rounded_rect(CornerFlags::ALL, xy, size, radius, color); + } + + pub fn draw_partially_rounded_rect( + &self, + corners: CornerFlags, + xy: Vec2, + size: Vec2, + radius: f32, + color: Color, + ) { + if corners.is_empty() { + self.draw_rect(xy, size, color); + return; + } + + let has_top = corners.intersects(CornerFlags::TOP); + let has_bottom = corners.intersects(CornerFlags::BOTTOM); + let edge_size = Vec2::new(size.x, radius); + 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); + if has_bottom { + let mut bottom_xy = xy; + let mut bottom_size = edge_size; - self.draw_rect(bl, inner_size, color); + if corners.contains(CornerFlags::BOTTOM_LEFT) { + bottom_xy.x += radius; + bottom_size.x -= radius; + self.draw_quarter_circle(Corner::BottomLeft, bl, radius, color); + } + + if corners.contains(CornerFlags::BOTTOM_RIGHT) { + bottom_size.x -= radius; + self.draw_quarter_circle(Corner::BottomRight, br, radius, color); + } + + self.draw_rect(bottom_xy, bottom_size, color); + } + + if has_top { + let mut top_xy = Vec2::new(xy.x, xy.y + size.y - radius); + let mut top_size = edge_size; + + if corners.contains(CornerFlags::TOP_LEFT) { + top_xy.x += radius; + top_size.x -= radius; + self.draw_quarter_circle(Corner::TopLeft, tl, radius, color); + } + + if corners.contains(CornerFlags::TOP_RIGHT) { + top_size.x -= radius; + self.draw_quarter_circle(Corner::TopRight, tr, radius, color); + } + + self.draw_rect(top_xy, top_size, color); + } + + let mut inner_xy = xy; + let mut inner_size = size; + + if has_bottom { + inner_xy.y += radius; + inner_size.y -= radius; + } + + if has_top { + inner_size.y -= radius; + } + + self.draw_rect(inner_xy, inner_size, color); } pub fn with_offset(&self, offset: Vec2) -> Self { diff --git a/src/widgets.rs b/src/widgets.rs index 5c47fa9..f9ef9d7 100644 --- a/src/widgets.rs +++ b/src/widgets.rs @@ -1,5 +1,5 @@ use crate::anim::Animation; -use crate::draw::{Corner, DrawContext}; +use crate::draw::{Corner, CornerFlags, DrawContext}; use crate::{Color, CursorEventKind, Vec2}; use keyframe::functions::*; @@ -93,6 +93,8 @@ impl Widget for RoundButton { pub struct RectButton { pub pos: Vec2, pub size: Vec2, + pub rounded_corners: CornerFlags, + pub radius: f32, pub was_clicked: bool, pub is_selected: bool, pub is_hovering: bool, @@ -110,10 +112,12 @@ 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(pos: Vec2, size: Vec2, rounded_corners: CornerFlags, radius: f32) -> Self { Self { pos, size, + rounded_corners, + radius, was_clicked: false, is_selected: false, is_hovering: false, @@ -134,7 +138,13 @@ 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.pos, + self.size, + self.radius, + self.color_anim.get(), + ); } fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) { @@ -555,7 +565,15 @@ impl TabMenu { 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() + }; + + tabs.push(RectButton::new(pos, tab_size, corners, radius)); } Self { tabs }