rounded corners and bug fixes

This commit is contained in:
2025-07-03 13:41:02 -05:00
parent 2f9d52e15e
commit dc7d897916
7 changed files with 198 additions and 69 deletions
+16 -14
View File
@@ -5,19 +5,30 @@ use crate::{
widget::{Element, Widget}, widget::{Element, Widget},
}; };
#[derive(Default)]
pub struct DivStyle { pub struct DivStyle {
background_color: u32, pub background_color: u32,
pub corner_radius: usize,
} }
#[derive(Default)]
pub struct Div { pub struct Div {
children: Vec<Arc<Widget>>, pub children: Vec<Arc<Widget>>,
style: DivStyle, pub style: DivStyle,
} }
impl Element for Div { impl Element for Div {
fn render(&mut self, scope_parent: &mut crate::render::RenderScope) { fn render(&mut self, scope_parent: &mut crate::render::RenderScope) {
let (w, h) = scope_parent.get_size_or_parent(); let (w, h) = scope_parent.get_size_or_parent();
scope_parent.draw_rect(0, 0, w, h, self.style.background_color); scope_parent.draw_rect_rounded(
0,
0,
w,
h,
self.style.corner_radius,
self.style.background_color,
);
let mut scope = RenderScope::new(w, h); let mut scope = RenderScope::new(w, h);
for elem in &self.children { for elem in &self.children {
@@ -31,7 +42,7 @@ impl Element for Div {
if let Some(t) = elem.get() { if let Some(t) = elem.get() {
scope.set_transform(&t); scope.set_transform(&t);
} }
scope_parent.merge(scope.clone(), 0, 0); scope_parent.merge(scope.clone());
} }
} }
@@ -45,15 +56,6 @@ impl Element for Div {
} }
impl Div { impl Div {
pub fn new() -> Self {
Self {
children: Vec::new(),
style: DivStyle {
background_color: 0xF00000,
},
}
}
pub fn draw<E: Element + 'static>(&mut self, element: E) -> &Arc<Widget> { pub fn draw<E: Element + 'static>(&mut self, element: E) -> &Arc<Widget> {
self.children.push(Arc::new(Widget::new(Box::new(element)))); self.children.push(Arc::new(Widget::new(Box::new(element))));
self.children.last().unwrap() self.children.last().unwrap()
+8 -1
View File
@@ -10,5 +10,12 @@ use crate::{style::RawTransform, widget::Widget};
pub trait Extension { pub trait Extension {
fn init(&mut self, _widgets: &Vec<Arc<Widget>>) {} fn init(&mut self, _widgets: &Vec<Arc<Widget>>) {}
fn render(&mut self, _widgets: &Arc<Widget>, _transform: RawTransform, _win: &Window) {} fn before_render(
&mut self,
_widgets: &Vec<Arc<Widget>>,
_transform: RawTransform,
_win: &Window,
) {
}
fn render(&mut self, _widget: &Arc<Widget>, _transform: RawTransform, _win: &Window) {}
} }
+29 -18
View File
@@ -7,32 +7,43 @@ use crate::{component, extensions::Extension, style::RawTransform, widget::Widge
pub struct MouseExtension(bool); pub struct MouseExtension(bool);
impl Extension for MouseExtension { impl Extension for MouseExtension {
fn render(&mut self, widget: &Arc<Widget>, transform: RawTransform, win: &Window) { fn before_render(
&mut self,
_widgets: &Vec<Arc<Widget>>,
_transform: RawTransform,
win: &Window,
) {
if win.get_mouse_down(MouseButton::Left) { if win.get_mouse_down(MouseButton::Left) {
if !self.0 { if !self.0 {
self.0 = true; self.0 = true;
if let Some((mx, my)) = win.get_mouse_pos(minifb::MouseMode::Discard) {
let (mx, my) = (mx as usize, my as usize);
if mx >= transform.x
&& my >= transform.y
&& mx <= transform.x + transform.width
&& mx <= transform.y + transform.height
{
std::thread::spawn({
let widget = widget.clone();
move || {
if let Some(on_click) = widget.get::<OnClick>() {
(on_click.0)(&widget)
}
}
});
}
}
} }
} else { } else {
self.0 = false; self.0 = false;
} }
} }
fn render(&mut self, widget: &Arc<Widget>, transform: RawTransform, win: &Window) {
if !self.0 {
return;
}
if let Some((mx, my)) = win.get_mouse_pos(minifb::MouseMode::Discard) {
let (mx, my) = (mx as usize, my as usize);
if my >= transform.y
&& mx < transform.x + transform.width
&& my < transform.y + transform.height
{
std::thread::spawn({
let widget = widget.clone();
move || {
if let Some(on_click) = widget.get::<OnClick>() {
(on_click.0)(&widget)
}
}
});
}
}
}
} }
impl MouseExtension { impl MouseExtension {
+6
View File
@@ -62,6 +62,12 @@ impl Screen {
let (w, h) = self.window.get_size(); let (w, h) = self.window.get_size();
let mut scope = RenderScope::new(w, h); let mut scope = RenderScope::new(w, h);
for ext in &self.extensions {
ext.lock()
.unwrap()
.before_render(&self.widgets, scope.get_transform(), &self.window);
}
for elem in &self.widgets { for elem in &self.widgets {
scope.clear(); scope.clear();
if let Some(t) = elem.get() { if let Some(t) = elem.get() {
+29 -13
View File
@@ -1,7 +1,7 @@
use minifb::{Window, WindowOptions}; use minifb::{Window, WindowOptions};
use osgui::{ use osgui::{
elements::div::Div, elements::div::{Div, DivStyle},
extensions::{mouse::MouseExtension, velocity::VelocityExtension}, extensions::mouse::{MouseExtension, OnClick},
style::Transform, style::Transform,
Screen, Screen,
}; };
@@ -9,8 +9,8 @@ use osgui::{
fn main() { fn main() {
let window = Window::new( let window = Window::new(
"minifb example", "minifb example",
200, 1920,
200, 1080,
WindowOptions { WindowOptions {
resize: false, resize: false,
..Default::default() ..Default::default()
@@ -19,17 +19,33 @@ fn main() {
.unwrap(); .unwrap();
let mut app = Screen::new(window); let mut app = Screen::new(window);
app.extension(VelocityExtension);
app.extension(MouseExtension::new()); app.extension(MouseExtension::new());
let mut d = Div::new(); milestones(&mut app);
d.draw("test".to_string()).component(Transform::center());
d.draw("abc".to_string())
.component(Transform::center().margin(0, 25));
app.draw(d)
.component(Transform::center().dimensions(150, 150));
app.run().unwrap(); app.run().unwrap();
} }
fn milestones(app: &mut Screen) {
for i in 0..4 {
let mut milestone = Div {
style: DivStyle {
background_color: 0x1D1D1D,
corner_radius: 24,
..Default::default()
},
..Default::default()
};
milestone
.draw(format!("My Milestone"))
.component(Transform::center());
app.draw(milestone)
.component(Transform::new().dimensions(873, 194).pos(
osgui::style::Position::Center,
osgui::style::Position::Const(145 + ((194 + 30) * i)),
))
.component(OnClick(|_| println!("Clicked!")));
}
}
+104 -23
View File
@@ -11,7 +11,8 @@ const FONT: &[u8] = include_bytes!("../assets/DejaVuSans.ttf");
pub enum RenderMethod { pub enum RenderMethod {
Text(String, usize, usize, f32, u32), Text(String, usize, usize, f32, u32),
Rectangle(usize, usize, usize, usize, u32), Rectangle(usize, usize, usize, usize, u32),
Merge(RenderScope, usize, usize), RoundedRectangle(usize, usize, usize, usize, usize, u32),
Merge(RenderScope),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -37,6 +38,9 @@ impl RenderScope {
} }
pub fn draw_text(&mut self, x: usize, y: usize, scale: f32, text: &str, color: u32) { pub fn draw_text(&mut self, x: usize, y: usize, scale: f32, text: &str, color: u32) {
if color == 0 {
return;
}
self.render_stack self.render_stack
.push(RenderMethod::Text(text.to_string(), x, y, scale, color)); .push(RenderMethod::Text(text.to_string(), x, y, scale, color));
let font = Font::try_from_bytes(FONT).unwrap(); let font = Font::try_from_bytes(FONT).unwrap();
@@ -47,6 +51,9 @@ impl RenderScope {
} }
pub fn draw_rect(&mut self, x: usize, y: usize, width: usize, height: usize, color: u32) { pub fn draw_rect(&mut self, x: usize, y: usize, width: usize, height: usize, color: u32) {
if color == 0 {
return;
}
self.render_stack self.render_stack
.push(RenderMethod::Rectangle(x, y, width, height, color)); .push(RenderMethod::Rectangle(x, y, width, height, color));
self.transform.width = self.transform.width.max(width); self.transform.width = self.transform.width.max(width);
@@ -54,10 +61,33 @@ impl RenderScope {
self.update_size(); self.update_size();
} }
pub fn merge(&mut self, scope: RenderScope, offset_x: usize, offset_y: usize) { pub fn draw_rect_rounded(
&mut self,
x: usize,
y: usize,
width: usize,
height: usize,
radius: usize,
color: u32,
) {
if color == 0 {
return;
}
if radius == 0 {
self.draw_rect(x, y, width, height, color);
return;
}
self.render_stack.push(RenderMethod::RoundedRectangle(
x, y, width, height, radius, color,
));
self.transform.width = self.transform.width.max(width);
self.transform.height = self.transform.height.max(height);
self.update_size();
}
pub fn merge(&mut self, scope: RenderScope) {
let (w, h) = scope.get_max_size(); let (w, h) = scope.get_max_size();
self.render_stack self.render_stack.push(RenderMethod::Merge(scope));
.push(RenderMethod::Merge(scope, offset_x, offset_y));
self.transform.width = self.transform.width.max(w as usize); self.transform.width = self.transform.width.max(w as usize);
self.transform.height = self.transform.height.max(h as usize); self.transform.height = self.transform.height.max(h as usize);
self.update_size(); self.update_size();
@@ -67,14 +97,14 @@ impl RenderScope {
// After draw // After draw
impl RenderScope { impl RenderScope {
pub fn draw(&mut self) { pub fn draw(&mut self) {
let mut tmp = self.buffer.clone(); let mut tmp = std::mem::take(&mut self.buffer);
self.draw_buf(&mut tmp, 0, 0); self.draw_buf(&mut tmp);
self.buffer = tmp; self.buffer = tmp;
} }
pub fn draw_buf(&self, buf: &mut Vec<Vec<u32>>, offset_x: usize, offset_y: usize) { pub fn draw_buf(&self, buf: &mut Vec<Vec<u32>>) {
let offset_x = self.transform.x + offset_x; let offset_x = self.transform.x;
let offset_y = self.transform.y + offset_y; let offset_y = self.transform.y;
for m in &self.render_stack { for m in &self.render_stack {
match m { match m {
@@ -86,6 +116,57 @@ impl RenderScope {
} }
} }
&RenderMethod::RoundedRectangle(px, py, width, height, radius, color) => {
let radius = radius as i32;
let w = width as i32;
let h = height as i32;
let start_x = offset_x as i32 + px as i32;
let start_y = offset_y as i32 + py as i32;
for y in 0..h {
for x in 0..w {
let dx = x;
let dy = y;
let in_top_left = dx < radius && dy < radius;
let in_top_right = dx >= w - radius && dy < radius;
let in_bottom_left = dx < radius && dy >= h - radius;
let in_bottom_right = dx >= w - radius && dy >= h - radius;
let dist = |cx: i32, cy: i32| -> bool {
let dx = dx - cx;
let dy = dy - cy;
dx * dx + dy * dy <= radius * radius
};
let draw = match (
in_top_left,
in_top_right,
in_bottom_left,
in_bottom_right,
) {
(true, _, _, _) => dist(radius - 1, radius - 1),
(_, true, _, _) => dist(w - radius, radius - 1),
(_, _, true, _) => dist(radius - 1, h - radius),
(_, _, _, true) => dist(w - radius, h - radius),
_ => true, // middle or edges outside corner
};
if draw {
let global_x = start_x + x;
let global_y = start_y + y;
if global_x >= 0
&& global_x < self.parent_width as i32
&& global_y >= 0
&& global_y < self.parent_height as i32
{
buf[global_y as usize][global_x as usize] = color;
}
}
}
}
}
RenderMethod::Text(text, px, py, scale, base_color) => { RenderMethod::Text(text, px, py, scale, base_color) => {
let font = Font::try_from_bytes(FONT).unwrap(); let font = Font::try_from_bytes(FONT).unwrap();
let scale = Scale::uniform(*scale); let scale = Scale::uniform(*scale);
@@ -113,26 +194,26 @@ impl RenderScope {
return; return;
} }
let x = gx as i32 + bb.min.x + *px as i32; let x = (gx as i32 + bb.min.x + *px as i32) as usize;
let y = gy as i32 + bb.min.y + *py as i32; let y = (gy as i32 + bb.min.y + *py as i32) as usize;
if (0..self.parent_width as i32).contains(&x) if let Some(row) = buf.get_mut(y) {
&& (0..self.parent_height as i32).contains(&y) if let Some(col) = row.get_mut(x) {
{ let r = (r_base * v) as u32;
let (x, y) = (x as usize, y as usize); let g = (g_base * v) as u32;
let b = (b_base * v) as u32;
let r = (r_base * v) as u32; *col = (r << 16) | (g << 8) | b;
let g = (g_base * v) as u32; }
let b = (b_base * v) as u32;
buf[y][x] = (r << 16) | (g << 8) | b;
} }
}); });
} }
} }
} }
RenderMethod::Merge(scope, ox, oy) => { RenderMethod::Merge(scope) => {
scope.draw_buf(buf, *ox + self.transform.x, *oy + self.transform.y); let mut scope = scope.clone();
scope.transform.x += self.transform.x;
scope.transform.y += self.transform.y;
scope.draw_buf(buf);
} }
} }
} }
+6
View File
@@ -64,6 +64,12 @@ impl Transform {
self self
} }
pub fn pos(mut self, x: Position, y: Position) -> Self {
self.x = x;
self.y = y;
self
}
pub fn dimensions(mut self, width: usize, height: usize) -> Self { pub fn dimensions(mut self, width: usize, height: usize) -> Self {
self.width = Dimension::Const(width); self.width = Dimension::Const(width);
self.height = Dimension::Const(height); self.height = Dimension::Const(height);