From 39e17a756229ece170a0f676e86fc936b9b0c92c Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 24 Apr 2020 18:32:27 +0200 Subject: [PATCH] Add painter region as example --- emigui/src/color.rs | 3 +- emigui/src/emigui.rs | 5 --- emigui/src/example_app.rs | 66 ++++++++++++++++++++++++++++++++++++++- emigui/src/id.rs | 4 +-- emigui/src/mesher.rs | 3 +- emigui/src/region.rs | 45 +++++++++++++++++++------- example_wasm/src/lib.rs | 2 +- 7 files changed, 105 insertions(+), 23 deletions(-) diff --git a/emigui/src/color.rs b/emigui/src/color.rs index 31b70818..3f4e0eaa 100644 --- a/emigui/src/color.rs +++ b/emigui/src/color.rs @@ -31,8 +31,9 @@ pub const fn gray(l: u8, a: u8) -> Color { } } -pub const WHITE: Color = srgba(255, 255, 255, 255); pub const BLACK: Color = srgba(0, 0, 0, 255); +pub const LIGHT_GRAY: Color = srgba(220, 220, 220, 255); +pub const WHITE: Color = srgba(255, 255, 255, 255); pub const RED: Color = srgba(255, 0, 0, 255); pub const GREEN: Color = srgba(0, 255, 0, 255); pub const BLUE: Color = srgba(0, 0, 255, 255); diff --git a/emigui/src/emigui.rs b/emigui/src/emigui.rs index 34287bff..2b378547 100644 --- a/emigui/src/emigui.rs +++ b/emigui/src/emigui.rs @@ -112,11 +112,6 @@ impl Emigui { } else { region.add_label("mouse_pos: None"); } - region.add(label!( - "region cursor: {} x {}", - region.cursor().x, - region.cursor().y, - )); region.add(label!("num_batches: {}", self.stats.num_batches)); region.add(label!("num_vertices: {}", self.stats.num_vertices)); region.add(label!("num_triangles: {}", self.stats.num_triangles)); diff --git a/emigui/src/example_app.rs b/emigui/src/example_app.rs index 165edf9a..b2c7731e 100644 --- a/emigui/src/example_app.rs +++ b/emigui/src/example_app.rs @@ -17,6 +17,8 @@ pub struct ExampleApp { num_columns: usize, slider_value: usize, + + painting: Painting, } impl Default for ExampleApp { @@ -33,6 +35,8 @@ impl Default for ExampleApp { num_columns: 2, slider_value: 100, + + painting: Default::default(), } } } @@ -51,7 +55,7 @@ impl ExampleApp { }); CollapsingHeader::new("Widgets") - .default_open() + // .default_open() .show(region, |region| { region.horizontal(Align::Min, |region| { region.add(label!("Text can have").text_color(srgba(110, 255, 110, 255))); @@ -147,6 +151,10 @@ impl ExampleApp { }); }); + CollapsingHeader::new("Painting") + .default_open() + .show(region, |region| self.painting.ui(region)); + region.collapsing("Name clash example", |region| { region.add_label("\ Regions that store state require unique identifiers so we can track their state between frames. \ @@ -173,6 +181,62 @@ impl ExampleApp { } } +#[derive(Default)] +struct Painting { + lines: Vec>, + current_line: Vec, +} + +impl Painting { + pub fn ui(&mut self, region: &mut Region) { + region.add_label("Draw with your mouse to paint"); + if region.add(Button::new("Clear")).clicked { + self.lines.clear(); + self.current_line.clear(); + } + + region.add_custom_contents(vec2(std::f32::INFINITY, 200.0), |region| { + let interact = region.reserve_space(region.available_space(), Some(region.id)); + region.clip_rect = interact.rect; // Make sure we don't paint out of bounds + + if interact.active { + if let Some(mouse_pos) = region.input().mouse_pos { + if self.current_line.last() != Some(&mouse_pos) { + self.current_line.push(mouse_pos); + } + } + } else if !self.current_line.is_empty() { + self.lines.push(std::mem::take(&mut self.current_line)); + } + + for line in &self.lines { + if line.len() >= 2 { + region.add_paint_cmd(PaintCmd::Line { + points: line.clone(), + color: LIGHT_GRAY, + width: 2.0, + }); + } + } + if self.current_line.len() >= 2 { + region.add_paint_cmd(PaintCmd::Line { + points: self.current_line.clone(), + color: WHITE, + width: 2.0, + }); + } + + // Frame it: + region.add_paint_cmd(PaintCmd::Rect { + rect: region.desired_rect, + corner_radius: 0.0, + fill_color: None, + outline: Some(Outline::new(1.0, WHITE)), + }); + }); + } +} + const LOREM_IPSUM: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst."; diff --git a/emigui/src/id.rs b/emigui/src/id.rs index 24df133e..4783e519 100644 --- a/emigui/src/id.rs +++ b/emigui/src/id.rs @@ -43,14 +43,14 @@ impl Id { Self(1) } - pub fn new(source: H) -> Id { + pub fn new(source: impl Hash) -> Id { use std::hash::Hasher; let mut hasher = DefaultHasher::new(); source.hash(&mut hasher); Id(hasher.finish()) } - pub fn with(self, child: H) -> Id { + pub fn with(self, child: impl Hash) -> Id { use std::hash::Hasher; let mut hasher = DefaultHasher::new(); hasher.write_u64(self.0); diff --git a/emigui/src/mesher.rs b/emigui/src/mesher.rs index 695ea02f..69eb2a29 100644 --- a/emigui/src/mesher.rs +++ b/emigui/src/mesher.rs @@ -451,10 +451,11 @@ pub fn mesh_command( fill_closed_path(out_mesh, options, &path.0, fill_color); } if let Some(outline) = outline { + let typ = if closed { Closed } else { Open }; paint_path( out_mesh, options, - Closed, + typ, &path.0, outline.color, outline.width, diff --git a/emigui/src/region.rs b/emigui/src/region.rs index af092d67..9fc10066 100644 --- a/emigui/src/region.rs +++ b/emigui/src/region.rs @@ -166,11 +166,25 @@ impl Region { // ------------------------------------------------------------------------ // Sub-regions: + /// Create a child region at the current cursor. + /// `size` is the desired size. + /// Actual size may be much smaller if `avilable_size()` is not enough. + /// Set `size` to `Vec::infinity()` to get as much space as possible. + /// Just because you ask for a lot of space does not mean you have to use it! + /// After `add_contents` is called the contents of `bounding_size` + /// will decide how much space will be used in the parent region. + pub fn add_custom_contents(&mut self, size: Vec2, add_contents: impl FnOnce(&mut Region)) { + let size = size.min(self.available_space()); + let child_rect = Rect::from_min_size(self.cursor, size); + let mut child_region = Region { + ..self.child_region(child_rect) + }; + add_contents(&mut child_region); + self.reserve_space_without_padding(child_region.bounding_size); + } + /// Create a child region which is indented to the right - pub fn indent(&mut self, id: Id, add_contents: F) - where - F: FnOnce(&mut Region), - { + pub fn indent(&mut self, id_source: impl Hash, add_contents: impl FnOnce(&mut Region)) { assert!( self.dir == Direction::Vertical, "You can only indent vertical layouts" @@ -178,7 +192,7 @@ impl Region { let indent = vec2(self.style.indent, 0.0); let child_rect = Rect::from_min_max(self.cursor + indent, self.desired_rect.max()); let mut child_region = Region { - id, + id: self.id.with(id_source), align: Align::Min, ..self.child_region(child_rect) }; @@ -297,6 +311,14 @@ impl Region { // ------------------------------------------------------------------------ + /// Check for clicks on this entire region (desired_rect) + pub fn interact(&self) -> InteractInfo { + self.ctx + .interact(self.layer, self.desired_rect, Some(self.id)) + } + + // ------------------------------------------------------------------------ + pub fn add(&mut self, widget: W) -> GuiResponse { widget.add_to(self) } @@ -355,15 +377,14 @@ impl Region { /// Will warn if the returned id is not guaranteed unique. /// Use this to generate widget ids for widgets that have persistent state in Memory. - /// If the child_id_source is not unique within this region + /// If the id_source is not unique within this region /// then an error will be printed at the current cursor position. - pub fn make_unique_id(&self, child_id_source: &IdSource) -> Id + pub fn make_unique_id(&self, id_source: &IdSource) -> Id where IdSource: Hash + std::fmt::Debug, { - let id = self.id.with(child_id_source); - self.ctx - .register_unique_id(id, child_id_source, self.cursor) + let id = self.id.with(id_source); + self.ctx.register_unique_id(id, id_source, self.cursor) } /// Make an Id that is unique to this positon. @@ -373,8 +394,8 @@ impl Region { self.id.with(&Id::from_pos(self.cursor)) } - pub fn make_child_id(&self, child_id: &H) -> Id { - self.id.with(child_id) + pub fn make_child_id(&self, id_seed: impl Hash) -> Id { + self.id.with(id_seed) } /// Show some text anywhere in the region. diff --git a/example_wasm/src/lib.rs b/example_wasm/src/lib.rs index 31a9a69b..8de51705 100644 --- a/example_wasm/src/lib.rs +++ b/example_wasm/src/lib.rs @@ -61,7 +61,7 @@ impl State { region.set_align(Align::Min); region.add_label("WebGl painter info:"); - region.indent(Id::new("webgl region"), |region| { + region.indent("webgl region", |region| { region.add_label(self.webgl_painter.debug_info()); });