diff --git a/src/elements/div.rs b/src/elements/div.rs index 8eb80ed..6be37a6 100644 --- a/src/elements/div.rs +++ b/src/elements/div.rs @@ -5,19 +5,30 @@ use crate::{ widget::{Element, Widget}, }; +#[derive(Default)] pub struct DivStyle { - background_color: u32, + pub background_color: u32, + pub corner_radius: usize, } +#[derive(Default)] pub struct Div { - children: Vec>, - style: DivStyle, + pub children: Vec>, + pub style: DivStyle, } impl Element for Div { fn render(&mut self, scope_parent: &mut crate::render::RenderScope) { 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); for elem in &self.children { @@ -31,7 +42,7 @@ impl Element for Div { if let Some(t) = elem.get() { 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 { - pub fn new() -> Self { - Self { - children: Vec::new(), - style: DivStyle { - background_color: 0xF00000, - }, - } - } - pub fn draw(&mut self, element: E) -> &Arc { self.children.push(Arc::new(Widget::new(Box::new(element)))); self.children.last().unwrap() diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs index 426c7e9..de21ad9 100644 --- a/src/extensions/mod.rs +++ b/src/extensions/mod.rs @@ -10,5 +10,12 @@ use crate::{style::RawTransform, widget::Widget}; pub trait Extension { fn init(&mut self, _widgets: &Vec>) {} - fn render(&mut self, _widgets: &Arc, _transform: RawTransform, _win: &Window) {} + fn before_render( + &mut self, + _widgets: &Vec>, + _transform: RawTransform, + _win: &Window, + ) { + } + fn render(&mut self, _widget: &Arc, _transform: RawTransform, _win: &Window) {} } diff --git a/src/extensions/mouse.rs b/src/extensions/mouse.rs index a4d93cf..d1d6e55 100644 --- a/src/extensions/mouse.rs +++ b/src/extensions/mouse.rs @@ -7,32 +7,43 @@ use crate::{component, extensions::Extension, style::RawTransform, widget::Widge pub struct MouseExtension(bool); impl Extension for MouseExtension { - fn render(&mut self, widget: &Arc, transform: RawTransform, win: &Window) { + fn before_render( + &mut self, + _widgets: &Vec>, + _transform: RawTransform, + win: &Window, + ) { if win.get_mouse_down(MouseButton::Left) { if !self.0 { 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::() { - (on_click.0)(&widget) - } - } - }); - } - } } } else { self.0 = false; } } + + fn render(&mut self, widget: &Arc, 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::() { + (on_click.0)(&widget) + } + } + }); + } + } + } } impl MouseExtension { diff --git a/src/lib.rs b/src/lib.rs index 64598af..4f86755 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,12 @@ impl Screen { let (w, h) = self.window.get_size(); 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 { scope.clear(); if let Some(t) = elem.get() { diff --git a/src/main.rs b/src/main.rs index 4e75e81..137de73 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use minifb::{Window, WindowOptions}; use osgui::{ - elements::div::Div, - extensions::{mouse::MouseExtension, velocity::VelocityExtension}, + elements::div::{Div, DivStyle}, + extensions::mouse::{MouseExtension, OnClick}, style::Transform, Screen, }; @@ -9,8 +9,8 @@ use osgui::{ fn main() { let window = Window::new( "minifb example", - 200, - 200, + 1920, + 1080, WindowOptions { resize: false, ..Default::default() @@ -19,17 +19,33 @@ fn main() { .unwrap(); let mut app = Screen::new(window); - app.extension(VelocityExtension); app.extension(MouseExtension::new()); - let mut d = Div::new(); - - 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)); + milestones(&mut app); 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!"))); + } +} diff --git a/src/render.rs b/src/render.rs index d324ce0..e6e3d0b 100644 --- a/src/render.rs +++ b/src/render.rs @@ -11,7 +11,8 @@ const FONT: &[u8] = include_bytes!("../assets/DejaVuSans.ttf"); pub enum RenderMethod { Text(String, usize, usize, f32, u32), Rectangle(usize, usize, usize, usize, u32), - Merge(RenderScope, usize, usize), + RoundedRectangle(usize, usize, usize, usize, usize, u32), + Merge(RenderScope), } #[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) { + if color == 0 { + return; + } self.render_stack .push(RenderMethod::Text(text.to_string(), x, y, scale, color)); 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) { + if color == 0 { + return; + } self.render_stack .push(RenderMethod::Rectangle(x, y, width, height, color)); self.transform.width = self.transform.width.max(width); @@ -54,10 +61,33 @@ impl RenderScope { 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(); - self.render_stack - .push(RenderMethod::Merge(scope, offset_x, offset_y)); + self.render_stack.push(RenderMethod::Merge(scope)); self.transform.width = self.transform.width.max(w as usize); self.transform.height = self.transform.height.max(h as usize); self.update_size(); @@ -67,14 +97,14 @@ impl RenderScope { // After draw impl RenderScope { pub fn draw(&mut self) { - let mut tmp = self.buffer.clone(); - self.draw_buf(&mut tmp, 0, 0); + let mut tmp = std::mem::take(&mut self.buffer); + self.draw_buf(&mut tmp); self.buffer = tmp; } - pub fn draw_buf(&self, buf: &mut Vec>, offset_x: usize, offset_y: usize) { - let offset_x = self.transform.x + offset_x; - let offset_y = self.transform.y + offset_y; + pub fn draw_buf(&self, buf: &mut Vec>) { + let offset_x = self.transform.x; + let offset_y = self.transform.y; for m in &self.render_stack { 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) => { let font = Font::try_from_bytes(FONT).unwrap(); let scale = Scale::uniform(*scale); @@ -113,26 +194,26 @@ impl RenderScope { return; } - let x = gx as i32 + bb.min.x + *px as i32; - let y = gy as i32 + bb.min.y + *py 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) as usize; - if (0..self.parent_width as i32).contains(&x) - && (0..self.parent_height as i32).contains(&y) - { - let (x, y) = (x as usize, y as usize); - - let r = (r_base * v) as u32; - let g = (g_base * v) as u32; - let b = (b_base * v) as u32; - - buf[y][x] = (r << 16) | (g << 8) | b; + if let Some(row) = buf.get_mut(y) { + if let Some(col) = row.get_mut(x) { + let r = (r_base * v) as u32; + let g = (g_base * v) as u32; + let b = (b_base * v) as u32; + *col = (r << 16) | (g << 8) | b; + } } }); } } } - RenderMethod::Merge(scope, ox, oy) => { - scope.draw_buf(buf, *ox + self.transform.x, *oy + self.transform.y); + RenderMethod::Merge(scope) => { + let mut scope = scope.clone(); + scope.transform.x += self.transform.x; + scope.transform.y += self.transform.y; + scope.draw_buf(buf); } } } diff --git a/src/style.rs b/src/style.rs index da3cdeb..b42dc53 100644 --- a/src/style.rs +++ b/src/style.rs @@ -64,6 +64,12 @@ impl Transform { 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 { self.width = Dimension::Const(width); self.height = Dimension::Const(height);