From 154424384ff3eeb0989fba155adb4f70b91d41a2 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sat, 25 Apr 2020 22:49:57 +0200 Subject: [PATCH] Implement Window as collection of Floating + Frame + Resize --- emigui/src/containers.rs | 21 ++ .../src/{ => containers}/collapsing_header.rs | 0 emigui/src/containers/floating.rs | 104 ++++++ emigui/src/{ => containers}/frame.rs | 4 +- emigui/src/{ => containers}/resize.rs | 73 ++++- emigui/src/{ => containers}/scroll_area.rs | 0 emigui/src/containers/window.rs | 80 +++++ emigui/src/context.rs | 2 +- emigui/src/example_app.rs | 2 +- emigui/src/floating.rs | 103 ------ emigui/src/lib.rs | 13 +- emigui/src/memory.rs | 38 ++- emigui/src/region.rs | 2 +- emigui/src/window.rs | 300 ------------------ example_glium/src/main.rs | 15 +- example_wasm/src/lib.rs | 3 +- 16 files changed, 301 insertions(+), 459 deletions(-) create mode 100644 emigui/src/containers.rs rename emigui/src/{ => containers}/collapsing_header.rs (100%) create mode 100644 emigui/src/containers/floating.rs rename emigui/src/{ => containers}/frame.rs (97%) rename emigui/src/{ => containers}/resize.rs (72%) rename emigui/src/{ => containers}/scroll_area.rs (100%) create mode 100644 emigui/src/containers/window.rs delete mode 100644 emigui/src/floating.rs delete mode 100644 emigui/src/window.rs diff --git a/emigui/src/containers.rs b/emigui/src/containers.rs new file mode 100644 index 00000000..9071f21a --- /dev/null +++ b/emigui/src/containers.rs @@ -0,0 +1,21 @@ +pub mod collapsing_header; +pub mod floating; +pub mod frame; +pub mod resize; +pub mod scroll_area; +pub mod window; + +pub use { + collapsing_header::CollapsingHeader, floating::Floating, frame::Frame, resize::Resize, + scroll_area::ScrollArea, window::Window, +}; + +// TODO +// pub trait Container { +// fn show(self, region: &mut Region, add_contents: impl FnOnce(&mut Region)); +// } + +// pub trait Container { +// fn begin(&mut self, parent: &mut Region) -> Region; +// fn end(self, parent: &mut Region, content: Region); +// } diff --git a/emigui/src/collapsing_header.rs b/emigui/src/containers/collapsing_header.rs similarity index 100% rename from emigui/src/collapsing_header.rs rename to emigui/src/containers/collapsing_header.rs diff --git a/emigui/src/containers/floating.rs b/emigui/src/containers/floating.rs new file mode 100644 index 00000000..aec7ffdc --- /dev/null +++ b/emigui/src/containers/floating.rs @@ -0,0 +1,104 @@ +//! A Floating is a region that has no parent, it floats on the background. +//! It is potentioally movable. +//! It has no frame or own size. +//! It is the foundation for a window + +use std::{fmt::Debug, hash::Hash, sync::Arc}; + +use crate::*; + +#[derive(Clone, Copy, Debug)] +pub struct State { + /// Last known pos + pub pos: Pos2, + + /// Last know size. Used for catching clicks. + pub size: Vec2, +} + +// TODO: rename Floating to something else. +#[derive(Clone, Copy, Debug)] +pub struct Floating { + id: Id, + movable: bool, + default_pos: Option, +} + +impl Floating { + pub fn new(id_source: impl Hash) -> Self { + Self { + id: Id::new(id_source), + movable: true, + default_pos: None, + } + } + + pub fn movable(mut self, movable: bool) -> Self { + self.movable = movable; + self + } + + pub fn default_pos(mut self, default_pos: Pos2) -> Self { + self.default_pos = Some(default_pos); + self + } +} + +impl Floating { + pub fn show(self, ctx: &Arc, add_contents: impl FnOnce(&mut Region)) { + let default_pos = self.default_pos.unwrap_or_else(|| pos2(100.0, 100.0)); // TODO + let id = ctx.register_unique_id(self.id, "Floating", default_pos); + let layer = Layer::Window(id); + + let (mut state, _is_new) = match ctx.memory.lock().get_floating(id) { + Some(state) => (state, false), + None => { + let state = State { + pos: default_pos, + size: Vec2::zero(), + }; + (state, true) + } + }; + state.pos = state.pos.round(); + + let mut region = Region::new( + ctx.clone(), + layer, + id, + Rect::from_min_size(state.pos, Vec2::infinity()), + ); + add_contents(&mut region); + state.size = region.bounding_size().ceil(); + + let rect = Rect::from_min_size(state.pos, state.size); + let move_interact = ctx.interact(layer, &rect, Some(id.with("move"))); + + if move_interact.active { + state.pos += ctx.input().mouse_move; + } + + // Constrain to screen: + let margin = 32.0; + state.pos = state.pos.max(pos2(margin - state.size.x, 0.0)); + state.pos = state.pos.min(pos2( + ctx.input.screen_size.x - margin, + ctx.input.screen_size.y - margin, + )); + + state.pos = state.pos.round(); + + if move_interact.active || mouse_pressed_on_floating(ctx, id) { + ctx.memory.lock().move_floating_to_top(id); + } + ctx.memory.lock().set_floating_state(id, state); + } +} + +fn mouse_pressed_on_floating(ctx: &Context, id: Id) -> bool { + if let Some(mouse_pos) = ctx.input.mouse_pos { + ctx.input.mouse_pressed && ctx.memory.lock().layer_at(mouse_pos) == Layer::Window(id) + } else { + false + } +} diff --git a/emigui/src/frame.rs b/emigui/src/containers/frame.rs similarity index 97% rename from emigui/src/frame.rs rename to emigui/src/containers/frame.rs index a1fa4f83..ee478359 100644 --- a/emigui/src/frame.rs +++ b/emigui/src/containers/frame.rs @@ -1,8 +1,8 @@ //! Frame container -//! + use crate::*; -#[derive(Default)] +#[derive(Clone, Debug, Default)] pub struct Frame {} impl Frame { diff --git a/emigui/src/resize.rs b/emigui/src/containers/resize.rs similarity index 72% rename from emigui/src/resize.rs rename to emigui/src/containers/resize.rs index 00148557..2f5d0982 100644 --- a/emigui/src/resize.rs +++ b/emigui/src/containers/resize.rs @@ -8,6 +8,9 @@ pub struct State { #[derive(Clone, Copy, Debug)] pub struct Resize { + /// If false, we are no enabled + resizable: bool, + // Will still try to stay within parent region bounds min_size: Vec2, max_size: Vec2, @@ -15,24 +18,28 @@ pub struct Resize { default_size: Vec2, // If true, won't allow you to make window so big that it creates spacing - shrink_width_to_fit_content: bool, - shrink_height_to_fit_content: bool, + auto_shrink_width: bool, + auto_shrink_height: bool, // If true, won't allow you to resize smaller than that everything fits. expand_width_to_fit_content: bool, expand_height_to_fit_content: bool, + + handle_offset: Vec2, } impl Default for Resize { fn default() -> Self { Self { + resizable: true, min_size: Vec2::splat(32.0), max_size: Vec2::infinity(), default_size: vec2(f32::INFINITY, 200.0), // TODO - shrink_width_to_fit_content: false, - shrink_height_to_fit_content: false, + auto_shrink_width: false, + auto_shrink_height: false, expand_width_to_fit_content: true, expand_height_to_fit_content: true, + handle_offset: Default::default(), } } } @@ -48,6 +55,35 @@ impl Resize { self } + pub fn min_size(mut self, min_size: Vec2) -> Self { + self.min_size = min_size; + self + } + + pub fn max_size(mut self, max_size: Vec2) -> Self { + self.max_size = max_size; + self + } + + /// Can you resize it with the mouse? + /// Note that a window can still auto-resize + pub fn resizable(mut self, resizable: bool) -> Self { + self.resizable = resizable; + self + } + + pub fn fixed_size(mut self, size: Vec2) -> Self { + self.auto_shrink_width = false; + self.auto_shrink_height = false; + self.expand_width_to_fit_content = false; + self.expand_height_to_fit_content = false; + self.default_size = size; + self.min_size = size; + self.max_size = size; + self.resizable = false; + self + } + pub fn as_wide_as_possible(mut self) -> Self { self.min_size.x = f32::INFINITY; self @@ -60,11 +96,31 @@ impl Resize { self.expand_height_to_fit_content = auto_expand; self } + + /// Offset the position of the resize handle by this much + pub fn handle_offset(mut self, handle_offset: Vec2) -> Self { + self.handle_offset = handle_offset; + self + } + + pub fn auto_shrink_width(mut self, auto_shrink_width: bool) -> Self { + self.auto_shrink_width = auto_shrink_width; + self + } + + pub fn auto_shrink_height(mut self, auto_shrink_height: bool) -> Self { + self.auto_shrink_height = auto_shrink_height; + self + } } // TODO: a common trait for Things that follow this pattern impl Resize { pub fn show(mut self, region: &mut Region, add_contents: impl FnOnce(&mut Region)) { + if !self.resizable { + return add_contents(region); + } + let id = region.make_child_id("scroll"); self.min_size = self.min_size.min(region.available_space()); self.max_size = self.max_size.min(region.available_space()); @@ -84,7 +140,10 @@ impl Resize { // Resize-corner: let corner_size = Vec2::splat(16.0); // TODO: style - let corner_rect = Rect::from_min_size(position + state.size - corner_size, corner_size); + let corner_rect = Rect::from_min_size( + position + state.size + self.handle_offset - corner_size, + corner_size, + ); let corner_interact = region.interact_rect(&corner_rect, id.with("corner")); if corner_interact.active { @@ -109,10 +168,10 @@ impl Resize { // ------------------------------ - if self.shrink_width_to_fit_content { + if self.auto_shrink_width { state.size.x = state.size.x.min(desired_size.x); } - if self.shrink_height_to_fit_content { + if self.auto_shrink_height { state.size.y = state.size.y.min(desired_size.y); } if self.expand_width_to_fit_content || is_new { diff --git a/emigui/src/scroll_area.rs b/emigui/src/containers/scroll_area.rs similarity index 100% rename from emigui/src/scroll_area.rs rename to emigui/src/containers/scroll_area.rs diff --git a/emigui/src/containers/window.rs b/emigui/src/containers/window.rs new file mode 100644 index 00000000..703fa01e --- /dev/null +++ b/emigui/src/containers/window.rs @@ -0,0 +1,80 @@ +use std::sync::Arc; + +use crate::{widgets::*, *}; + +use super::*; + +// TODO: separate out resizing into a contained and reusable Resize-region. +#[derive(Clone, Debug)] +pub struct Window { + title: String, + floating: Floating, + frame: Frame, + resize: Resize, +} + +impl Window { + pub fn new(title: impl Into) -> Self { + let title = title.into(); + Self { + title: title.clone(), + floating: Floating::new(title), + frame: Frame::default(), + resize: Resize::default() + .handle_offset(Vec2::splat(4.0)) + .auto_shrink_height(true), + } + } + + pub fn default_pos(mut self, default_pos: Pos2) -> Self { + self.floating = self.floating.default_pos(default_pos); + self + } + + pub fn default_size(mut self, default_size: Vec2) -> Self { + self.resize = self.resize.default_size(default_size); + self + } + + pub fn min_size(mut self, min_size: Vec2) -> Self { + self.resize = self.resize.min_size(min_size); + self + } + + pub fn max_size(mut self, max_size: Vec2) -> Self { + self.resize = self.resize.max_size(max_size); + self + } + + pub fn fixed_size(mut self, size: Vec2) -> Self { + self.resize = self.resize.fixed_size(size); + self + } + + /// Can you resize it with the mouse? + /// Note that a window can still auto-resize + pub fn resizable(mut self, resizable: bool) -> Self { + self.resize = self.resize.resizable(resizable); + self + } +} + +impl Window { + pub fn show(self, ctx: &Arc, add_contents: impl FnOnce(&mut Region)) { + let Window { + title, + floating, + frame, + resize, + } = self; + floating.show(ctx, |region| { + frame.show(region, |region| { + resize.show(region, |region| { + region.add(Label::new(title).text_style(TextStyle::Heading)); + region.add(Separator::new().line_width(1.0)); // TODO: nicer way to split window title from contents + add_contents(region); + }); + }); + }); + } +} diff --git a/emigui/src/context.rs b/emigui/src/context.rs index ab5a727d..c66ae1dc 100644 --- a/emigui/src/context.rs +++ b/emigui/src/context.rs @@ -76,7 +76,7 @@ impl Context { pub fn drain_paint_lists(&self) -> Vec<(Rect, PaintCmd)> { let memory = self.memory.lock(); - self.graphics.lock().drain(&memory.window_order).collect() + self.graphics.lock().drain(&memory.floating_order).collect() } /// Is the user interacting with anything? diff --git a/emigui/src/example_app.rs b/emigui/src/example_app.rs index 857d09d3..194d478a 100644 --- a/emigui/src/example_app.rs +++ b/emigui/src/example_app.rs @@ -1,4 +1,4 @@ -use crate::{color::*, widgets::*, *}; +use crate::{color::*, containers::*, widgets::*, *}; /// Showcase some region code pub struct ExampleApp { diff --git a/emigui/src/floating.rs b/emigui/src/floating.rs deleted file mode 100644 index 2d3fc889..00000000 --- a/emigui/src/floating.rs +++ /dev/null @@ -1,103 +0,0 @@ -//! A Floating is a region that has no parent, it floats on the background. -//! It is potentioally movable. -//! It has no frame or own size. -//! It is the foundation for a window -//! -use std::{fmt::Debug, hash::Hash, sync::Arc}; - -use crate::*; - -// struct State { -// pos: Pos2, -// /// used for catching clickes -// size: Vec2, -// } - -type State = crate::window::State; - -#[derive(Clone, Copy, Debug)] -pub struct Floating { - movable: bool, - default_pos: Option, -} - -impl Floating { - pub fn new() -> Self { - Self { - movable: true, - default_pos: None, - } - } - - pub fn movable(mut self, movable: bool) -> Self { - self.movable = movable; - self - } -} - -impl Floating { - pub fn show( - self, - ctx: &Arc, - id_source: impl Copy + Debug + Hash, - add_contents: impl FnOnce(&mut Region), - ) { - let default_pos = self.default_pos.unwrap_or_else(|| pos2(100.0, 100.0)); // TODO - let id = ctx.make_unique_id(id_source, default_pos); - let layer = Layer::Window(id); - - let (mut state, _is_new) = match ctx.memory.lock().get_window(id) { - Some(state) => (state, false), - None => { - let state = State { - outer_pos: default_pos, - inner_size: Vec2::zero(), - outer_rect: Rect::from_min_size(default_pos, Vec2::zero()), - }; - (state, true) - } - }; - state.outer_pos = state.outer_pos.round(); - - let mut region = Region::new( - ctx.clone(), - layer, - id, - Rect::from_min_size(state.outer_pos, Vec2::infinity()), - ); - add_contents(&mut region); - let size = region.bounding_size().ceil(); - - state.outer_rect = Rect::from_min_size(state.outer_pos, size); - let move_interact = ctx.interact(layer, &state.outer_rect, Some(id.with("move"))); - - if move_interact.active { - state.outer_pos += ctx.input().mouse_move; - } - - // Constrain to screen: - let margin = 32.0; - state.outer_pos = state.outer_pos.max(pos2(margin - size.x, 0.0)); - state.outer_pos = state.outer_pos.min(pos2( - ctx.input.screen_size.x - margin, - ctx.input.screen_size.y - margin, - )); - state.outer_pos = state.outer_pos.round(); - - state.inner_size = size; - state.outer_rect = Rect::from_min_size(state.outer_pos, size); - - if move_interact.active || mouse_pressed_on_window(ctx, id) { - ctx.memory.lock().move_window_to_top(id); - } - ctx.memory.lock().set_window_state(id, state); - } -} - -fn mouse_pressed_on_window(ctx: &Context, id: Id) -> bool { - if let Some(mouse_pos) = ctx.input.mouse_pos { - ctx.input.mouse_pressed && ctx.memory.lock().layer_at(mouse_pos) == Layer::Window(id) - } else { - false - } -} diff --git a/emigui/src/lib.rs b/emigui/src/lib.rs index 6f7540a3..77d60295 100644 --- a/emigui/src/lib.rs +++ b/emigui/src/lib.rs @@ -6,15 +6,13 @@ extern crate serde; #[macro_use] // TODO: get rid of this extern crate serde_derive; -mod collapsing_header; pub mod color; +pub mod containers; mod context; mod emigui; pub mod example_app; -mod floating; mod font; mod fonts; -mod frame; mod id; mod layers; mod layout; @@ -22,22 +20,16 @@ pub mod math; mod memory; pub mod mesher; mod region; -mod resize; -mod scroll_area; mod style; mod texture_atlas; mod types; pub mod widgets; -mod window; pub use { crate::emigui::Emigui, - collapsing_header::CollapsingHeader, color::Color, context::Context, - floating::Floating, fonts::{FontDefinitions, Fonts, TextStyle}, - frame::Frame, id::Id, layers::*, layout::{Align, GuiResponse}, @@ -45,10 +37,7 @@ pub use { memory::Memory, mesher::{Mesh, PaintBatches, Vertex}, region::Region, - resize::Resize, - scroll_area::ScrollArea, style::Style, texture_atlas::Texture, types::*, - window::Window, }; diff --git a/emigui/src/memory.rs b/emigui/src/memory.rs index d4781988..d05211ef 100644 --- a/emigui/src/memory.rs +++ b/emigui/src/memory.rs @@ -1,6 +1,9 @@ use std::collections::HashMap; -use crate::{collapsing_header, resize, scroll_area, window, *}; +use crate::{ + containers::{collapsing_header, floating, resize, scroll_area}, + Id, Layer, Pos2, Rect, +}; #[derive(Clone, Debug, Default)] pub struct Memory { @@ -11,43 +14,44 @@ pub struct Memory { pub(crate) collapsing_headers: HashMap, pub(crate) scroll_areas: HashMap, pub(crate) resize: HashMap, - windows: HashMap, + floating: HashMap, /// Top is last - pub window_order: Vec, + pub floating_order: Vec, } impl Memory { - pub fn get_window(&mut self, id: Id) -> Option { - self.windows.get(&id).cloned() + pub fn get_floating(&mut self, id: Id) -> Option { + self.floating.get(&id).cloned() } - pub fn set_window_state(&mut self, id: Id, state: window::State) { - let did_insert = self.windows.insert(id, state).is_none(); + pub fn set_floating_state(&mut self, id: Id, state: floating::State) { + let did_insert = self.floating.insert(id, state).is_none(); if did_insert { - self.window_order.push(id); + self.floating_order.push(id); } } /// TODO: call once at the start of the frame for the current mouse pos pub fn layer_at(&self, pos: Pos2) -> Layer { - for window_id in self.window_order.iter().rev() { - if let Some(state) = self.windows.get(window_id) { - if state.outer_rect.contains(pos) { - return Layer::Window(*window_id); + for floating_id in self.floating_order.iter().rev() { + if let Some(state) = self.floating.get(floating_id) { + let rect = Rect::from_min_size(state.pos, state.size); + if rect.contains(pos) { + return Layer::Window(*floating_id); } } } Layer::Background } - pub fn move_window_to_top(&mut self, id: Id) { - if self.window_order.last() == Some(&id) { + pub fn move_floating_to_top(&mut self, id: Id) { + if self.floating_order.last() == Some(&id) { return; // common case early-out } - if let Some(index) = self.window_order.iter().position(|x| *x == id) { - self.window_order.remove(index); + if let Some(index) = self.floating_order.iter().position(|x| *x == id) { + self.floating_order.remove(index); } - self.window_order.push(id); + self.floating_order.push(id); } } diff --git a/emigui/src/region.rs b/emigui/src/region.rs index 48897f23..6c5fb533 100644 --- a/emigui/src/region.rs +++ b/emigui/src/region.rs @@ -1,6 +1,6 @@ use std::{hash::Hash, sync::Arc}; -use crate::{color::*, font::TextFragment, layout::*, widgets::*, *}; +use crate::{color::*, containers::*, font::TextFragment, layout::*, widgets::*, *}; /// Represents a region of the screen /// with a type of layout (horizontal or vertical). diff --git a/emigui/src/window.rs b/emigui/src/window.rs deleted file mode 100644 index c62f2474..00000000 --- a/emigui/src/window.rs +++ /dev/null @@ -1,300 +0,0 @@ -use std::sync::Arc; - -use crate::{mesher::Path, widgets::*, *}; - -#[derive(Clone, Copy, Debug)] -pub struct State { - /// Last known pos - pub outer_pos: Pos2, - pub inner_size: Vec2, - - /// used for catching clicks: - pub outer_rect: Rect, -} - -// TODO: separate out resizing into a contained and reusable Resize-region. -#[derive(Clone, Debug)] -pub struct Window { - /// The title of the window and by default the source of its identity. - title: String, - /// Put the window here the first time - default_pos: Option, - - /// Size of the window first time - default_size: Option, - - resizeable: bool, - - // If true, won't allow you to make window so big that it creates spacing - shrink_width_to_fit_content: bool, - shrink_height_to_fit_content: bool, - - // If true, won't allow you to resize smaller than that everything fits. - expand_width_to_fit_content: bool, - expand_height_to_fit_content: bool, - - min_size: Vec2, - max_size: Option, -} - -impl Default for Window { - fn default() -> Self { - Self { - title: "".to_owned(), - default_pos: None, - default_size: None, - resizeable: true, - shrink_width_to_fit_content: false, - shrink_height_to_fit_content: false, - expand_width_to_fit_content: true, - expand_height_to_fit_content: true, - min_size: Vec2::splat(16.0), - max_size: None, - } - } -} - -impl Window { - pub fn new(title: impl Into) -> Self { - Self { - title: title.into(), - ..Default::default() - } - } - - pub fn default_pos(mut self, default_pos: Pos2) -> Self { - self.default_pos = Some(default_pos); - self - } - - pub fn default_size(mut self, default_size: Vec2) -> Self { - self.default_size = Some(default_size); - self - } - - pub fn min_size(mut self, min_size: Vec2) -> Self { - self.min_size = min_size; - self - } - - pub fn max_size(mut self, max_size: Vec2) -> Self { - self.max_size = Some(max_size); - self - } - - pub fn fixed_size(mut self, size: Vec2) -> Self { - self.shrink_width_to_fit_content = false; - self.shrink_height_to_fit_content = false; - self.expand_width_to_fit_content = false; - self.expand_height_to_fit_content = false; - self.default_size = Some(size); - self.min_size = size; - self.max_size = Some(size); - self - } - - /// Can you resize it with the mouse? - /// Note that a window can still auto-resize - pub fn resizeable(mut self, resizeable: bool) -> Self { - self.resizeable = resizeable; - self - } - - // pub fn shrink_to_fit_content(mut self, shrink_to_fit_content: bool) -> Self { - // self.shrink_to_fit_content = shrink_to_fit_content; - // self - // } - - // pub fn expand_to_fit_content(mut self, expand_to_fit_content: bool) -> Self { - // self.expand_to_fit_content = expand_to_fit_content; - // self - // } -} - -impl Window { - pub fn show(self, ctx: &Arc, add_contents: impl FnOnce(&mut Region)) { - let style = ctx.style(); - let window_padding = style.window_padding; - - let default_pos = self.default_pos.unwrap_or_else(|| pos2(100.0, 100.0)); // TODO - let default_inner_size = self.default_size.unwrap_or_else(|| vec2(250.0, 250.0)); - - let id = ctx.make_unique_id(&self.title, default_pos); - - let (mut state, is_new) = match ctx.memory.lock().get_window(id) { - Some(state) => (state, false), - None => { - let state = State { - outer_pos: default_pos, - inner_size: default_inner_size, - outer_rect: Rect::from_min_size( - default_pos, - default_inner_size + 2.0 * window_padding, - ), - }; - (state, true) - } - }; - - state.outer_pos = state.outer_pos.round(); // TODO: round to pixel - - let min_inner_size = self.min_size; - let max_inner_size = self - .max_size - .unwrap_or(Pos2::default() + ctx.input.screen_size - state.outer_pos); - - let layer = Layer::Window(id); - let where_to_put_background = ctx.graphics.lock().layer(layer).len(); - - let inner_rect = Rect::from_min_size(state.outer_pos + window_padding, state.inner_size); - let mut contents_region = Region::new(ctx.clone(), layer, id, inner_rect); - // TODO: handle contents_region.clip_rect while resizing - - // Show top bar: - contents_region.add(Label::new(self.title).text_style(TextStyle::Heading)); - contents_region.add(Separator::new().line_width(1.0).extra(window_padding.x)); // TODO: nicer way to split window title from contents - - add_contents(&mut contents_region); - - // TODO: handle the last item_spacing in a nicer way - let desired_inner_size = contents_region.bounding_size() - style.item_spacing; - let desired_inner_size = desired_inner_size.ceil(); // Avoid rounding errors in math - - let mut new_inner_size = state.inner_size; - if self.shrink_width_to_fit_content { - new_inner_size.x = new_inner_size.x.min(desired_inner_size.x); - } - if self.shrink_height_to_fit_content { - new_inner_size.y = new_inner_size.y.min(desired_inner_size.y); - } - if self.expand_width_to_fit_content || is_new { - new_inner_size.x = new_inner_size.x.max(desired_inner_size.x); - } - if self.expand_height_to_fit_content || is_new { - new_inner_size.y = new_inner_size.y.max(desired_inner_size.y); - } - new_inner_size = new_inner_size.max(min_inner_size); - new_inner_size = new_inner_size.min(max_inner_size); - - let new_outer_size = new_inner_size + 2.0 * window_padding; - - let outer_rect = Rect::from_min_size(state.outer_pos, new_outer_size); - - let mut graphics = ctx.graphics.lock(); - - let corner_radius = style.window.corner_radius; - graphics.layer(layer).insert( - where_to_put_background, - ( - Rect::everything(), - PaintCmd::Rect { - corner_radius, - fill_color: Some(style.background_fill_color()), - outline: Some(Outline::new(1.0, color::WHITE)), - rect: outer_rect, - }, - ), - ); - - let corner_interact = if self.resizeable { - // Resize-corner: - let corner_center = outer_rect.max - Vec2::splat(corner_radius); - let corner_rect = Rect::from_min_size(corner_center, Vec2::splat(corner_radius)); - - let corner_interact = ctx.interact(layer, &corner_rect, Some(id.with("corner"))); - - graphics.layer(layer).push(( - Rect::everything(), - paint_resize_corner(corner_center, corner_radius, &style, &corner_interact), - )); - corner_interact - } else { - InteractInfo::default() - }; - - let win_interact = ctx.interact(layer, &outer_rect, Some(id.with("window"))); - - if corner_interact.active { - if let Some(mouse_pos) = ctx.input().mouse_pos { - let new_outer_size = - mouse_pos - state.outer_pos + 0.5 * corner_interact.rect.size(); - new_inner_size = new_outer_size - 2.0 * window_padding; - new_inner_size = new_inner_size.max(min_inner_size); - new_inner_size = new_inner_size.min(max_inner_size); - } - } else if win_interact.active { - state.outer_pos += ctx.input().mouse_move; - } - - if corner_interact.hovered || corner_interact.active { - ctx.output.lock().cursor_icon = CursorIcon::ResizeNwSe; - } - - state = State { - outer_pos: state.outer_pos, - inner_size: new_inner_size, - outer_rect, - }; - - // Constrain to screen: - let margin = 32.0; - state.outer_pos = state.outer_pos.max(pos2(margin - outer_rect.width(), 0.0)); - state.outer_pos = state.outer_pos.min(pos2( - ctx.input.screen_size.x - margin, - ctx.input.screen_size.y - margin, - )); - - if win_interact.active || corner_interact.active || mouse_pressed_on_window(ctx, id) { - ctx.memory.lock().move_window_to_top(id); - } - ctx.memory.lock().set_window_state(id, state); - } -} - -fn mouse_pressed_on_window(ctx: &Context, id: Id) -> bool { - if let Some(mouse_pos) = ctx.input.mouse_pos { - ctx.input.mouse_pressed && ctx.memory.lock().layer_at(mouse_pos) == Layer::Window(id) - } else { - false - } -} - -fn paint_resize_corner( - center: Pos2, - radius: f32, - style: &Style, - interact: &InteractInfo, -) -> PaintCmd { - if false { - // TODO: Path::circle_sector() or something - let quadrant = 0.0; // Bottom-right - let mut path = Path::default(); - path.add_point(center, vec2(0.0, -1.0)); - path.add_point(center + vec2(radius, 0.0), vec2(0.0, -1.0)); - path.add_circle_quadrant(center, radius, quadrant); - path.add_point(center + vec2(0.0, radius), vec2(-1.0, 0.0)); - path.add_point(center, vec2(-1.0, 0.0)); - PaintCmd::Path { - path, - closed: true, - fill_color: style.interact_fill_color(&interact), - outline: style.interact_outline(&interact), - } - } else { - let offset = 3.0; - let center = center; - let radius = radius - offset; - let quadrant = 0.0; // Bottom-right - let mut path = Path::default(); - path.add_circle_quadrant(center, radius, quadrant); - PaintCmd::Path { - path, - closed: false, - fill_color: None, - outline: Some(Outline::new( - style.interact_stroke_width(&interact), - style.interact_stroke_color(&interact), - )), - } - } -} diff --git a/example_glium/src/main.rs b/example_glium/src/main.rs index 68dc6233..ab239450 100644 --- a/example_glium/src/main.rs +++ b/example_glium/src/main.rs @@ -3,7 +3,7 @@ use std::time::{Duration, Instant}; use { - emigui::{example_app::ExampleApp, widgets::*, *}, + emigui::{containers::*, example_app::ExampleApp, widgets::*, *}, emigui_glium::Painter, glium::glutin, }; @@ -121,19 +121,6 @@ fn main() { emigui.ui(region); }); - Floating::new().show(region.ctx(), "Floating", |region| { - Frame::default().show(region, |region| { - Resize::default() - .default_size(vec2(300.0, 200.0)) - .auto_expand(true) - .show(region, |region| { - region.add(Label::new("Fake Window").text_style(TextStyle::Heading)); - region.add(Separator::new().line_width(1.0)); // TODO: nicer way to split window title from contents - region.add(label!("Floating Frame Resize")); - }); - }); - }); - let (output, paint_batches) = emigui.end_frame(); painter.paint_batches(&display, paint_batches, emigui.texture()); diff --git a/example_wasm/src/lib.rs b/example_wasm/src/lib.rs index 8de51705..3a4896a9 100644 --- a/example_wasm/src/lib.rs +++ b/example_wasm/src/lib.rs @@ -9,10 +9,11 @@ extern crate emigui_wasm; use { emigui::{ color::srgba, + containers::*, example_app::ExampleApp, label, widgets::{Label, Separator}, - Align, Emigui, RawInput, TextStyle, Window, *, + Align, Emigui, RawInput, TextStyle, *, }, emigui_wasm::now_sec, };