Add new Resize container
This commit is contained in:
parent
9ba5bea143
commit
3a430c8fc7
13 changed files with 262 additions and 31 deletions
|
@ -37,4 +37,5 @@ pub const WHITE: Color = srgba(255, 255, 255, 255);
|
||||||
pub const RED: Color = srgba(255, 0, 0, 255);
|
pub const RED: Color = srgba(255, 0, 0, 255);
|
||||||
pub const GREEN: Color = srgba(0, 255, 0, 255);
|
pub const GREEN: Color = srgba(0, 255, 0, 255);
|
||||||
pub const BLUE: Color = srgba(0, 0, 255, 255);
|
pub const BLUE: Color = srgba(0, 0, 255, 255);
|
||||||
|
pub const YELLOW: Color = srgba(255, 255, 0, 255);
|
||||||
pub const LIGHT_BLUE: Color = srgba(140, 160, 255, 255);
|
pub const LIGHT_BLUE: Color = srgba(140, 160, 255, 255);
|
||||||
|
|
|
@ -128,7 +128,7 @@ impl Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interact(&self, layer: Layer, rect: Rect, interaction_id: Option<Id>) -> InteractInfo {
|
pub fn interact(&self, layer: Layer, rect: &Rect, interaction_id: Option<Id>) -> InteractInfo {
|
||||||
let hovered = self.contains_mouse_pos(layer, &rect);
|
let hovered = self.contains_mouse_pos(layer, &rect);
|
||||||
|
|
||||||
let mut memory = self.memory.lock();
|
let mut memory = self.memory.lock();
|
||||||
|
@ -139,7 +139,7 @@ impl Context {
|
||||||
if memory.active_id.is_some() {
|
if memory.active_id.is_some() {
|
||||||
// Already clicked something else this frame
|
// Already clicked something else this frame
|
||||||
InteractInfo {
|
InteractInfo {
|
||||||
rect,
|
rect: *rect,
|
||||||
hovered,
|
hovered,
|
||||||
clicked: false,
|
clicked: false,
|
||||||
active: false,
|
active: false,
|
||||||
|
@ -147,7 +147,7 @@ impl Context {
|
||||||
} else {
|
} else {
|
||||||
memory.active_id = interaction_id;
|
memory.active_id = interaction_id;
|
||||||
InteractInfo {
|
InteractInfo {
|
||||||
rect,
|
rect: *rect,
|
||||||
hovered,
|
hovered,
|
||||||
clicked: false,
|
clicked: false,
|
||||||
active: true,
|
active: true,
|
||||||
|
@ -155,7 +155,7 @@ impl Context {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
InteractInfo {
|
InteractInfo {
|
||||||
rect,
|
rect: *rect,
|
||||||
hovered,
|
hovered,
|
||||||
clicked: false,
|
clicked: false,
|
||||||
active: false,
|
active: false,
|
||||||
|
@ -163,21 +163,21 @@ impl Context {
|
||||||
}
|
}
|
||||||
} else if self.input.mouse_released {
|
} else if self.input.mouse_released {
|
||||||
InteractInfo {
|
InteractInfo {
|
||||||
rect,
|
rect: *rect,
|
||||||
hovered,
|
hovered,
|
||||||
clicked: hovered && active,
|
clicked: hovered && active,
|
||||||
active,
|
active,
|
||||||
}
|
}
|
||||||
} else if self.input.mouse_down {
|
} else if self.input.mouse_down {
|
||||||
InteractInfo {
|
InteractInfo {
|
||||||
rect,
|
rect: *rect,
|
||||||
hovered: hovered && active,
|
hovered: hovered && active,
|
||||||
clicked: false,
|
clicked: false,
|
||||||
active,
|
active,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
InteractInfo {
|
InteractInfo {
|
||||||
rect,
|
rect: *rect,
|
||||||
hovered,
|
hovered,
|
||||||
clicked: false,
|
clicked: false,
|
||||||
active,
|
active,
|
||||||
|
@ -187,11 +187,11 @@ impl Context {
|
||||||
|
|
||||||
pub fn show_error(&self, pos: Pos2, text: &str) {
|
pub fn show_error(&self, pos: Pos2, text: &str) {
|
||||||
let align = (Align::Min, Align::Min);
|
let align = (Align::Min, Align::Min);
|
||||||
let layer = Layer::Popup; // TODO: Layer::Error
|
let layer = Layer::Popup; // TODO: Layer::Debug
|
||||||
let text_style = TextStyle::Monospace;
|
let text_style = TextStyle::Monospace;
|
||||||
let font = &self.fonts[text_style];
|
let font = &self.fonts[text_style];
|
||||||
let (text, size) = font.layout_multiline(text, f32::INFINITY);
|
let (text, size) = font.layout_multiline(text, f32::INFINITY);
|
||||||
let rect = align_rect(Rect::from_min_size(pos, size), align);
|
let rect = align_rect(&Rect::from_min_size(pos, size), align);
|
||||||
self.add_paint_cmd(
|
self.add_paint_cmd(
|
||||||
layer,
|
layer,
|
||||||
PaintCmd::Rect {
|
PaintCmd::Rect {
|
||||||
|
@ -204,6 +204,19 @@ impl Context {
|
||||||
self.add_text(layer, rect.min(), text_style, text, Some(color::RED));
|
self.add_text(layer, rect.min(), text_style, text, Some(color::RED));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn debug_text(&self, pos: Pos2, text: &str) {
|
||||||
|
let layer = Layer::Popup; // TODO: Layer::Debug
|
||||||
|
let align = (Align::Min, Align::Min);
|
||||||
|
self.floating_text(
|
||||||
|
layer,
|
||||||
|
pos,
|
||||||
|
text,
|
||||||
|
TextStyle::Monospace,
|
||||||
|
align,
|
||||||
|
Some(color::YELLOW),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Show some text anywhere on screen.
|
/// Show some text anywhere on screen.
|
||||||
/// To center the text at the given position, use `align: (Center, Center)`.
|
/// To center the text at the given position, use `align: (Center, Center)`.
|
||||||
pub fn floating_text(
|
pub fn floating_text(
|
||||||
|
@ -217,7 +230,7 @@ impl Context {
|
||||||
) -> Vec2 {
|
) -> Vec2 {
|
||||||
let font = &self.fonts[text_style];
|
let font = &self.fonts[text_style];
|
||||||
let (text, size) = font.layout_multiline(text, f32::INFINITY);
|
let (text, size) = font.layout_multiline(text, f32::INFINITY);
|
||||||
let rect = align_rect(Rect::from_min_size(pos, size), align);
|
let rect = align_rect(&Rect::from_min_size(pos, size), align);
|
||||||
self.add_text(layer, rect.min(), text_style, text, text_color);
|
self.add_text(layer, rect.min(), text_style, text, text_color);
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
use crate::{
|
use crate::{color::*, widgets::*, *};
|
||||||
color::*, label, math::*, widgets::*, Align, CollapsingHeader, Outline, PaintCmd, Region,
|
|
||||||
ScrollArea,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Showcase some region code
|
/// Showcase some region code
|
||||||
pub struct ExampleApp {
|
pub struct ExampleApp {
|
||||||
|
@ -150,9 +147,21 @@ impl ExampleApp {
|
||||||
});
|
});
|
||||||
|
|
||||||
CollapsingHeader::new("Painting")
|
CollapsingHeader::new("Painting")
|
||||||
.default_open()
|
// .default_open()
|
||||||
.show(region, |region| self.painting.ui(region));
|
.show(region, |region| self.painting.ui(region));
|
||||||
|
|
||||||
|
CollapsingHeader::new("Resize")
|
||||||
|
.default_open()
|
||||||
|
.show(region, |region| {
|
||||||
|
Resize::default()
|
||||||
|
.default_height(200.0)
|
||||||
|
// .as_wide_as_possible()
|
||||||
|
.show(region, |region| {
|
||||||
|
region.add(label!("This region can be resized!"));
|
||||||
|
region.add(label!("Just pull the handle on the bottom right"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
region.collapsing("Name clash example", |region| {
|
region.collapsing("Name clash example", |region| {
|
||||||
region.add_label("\
|
region.add_label("\
|
||||||
Regions that store state require unique identifiers so we can track their state between frames. \
|
Regions that store state require unique identifiers so we can track their state between frames. \
|
||||||
|
|
|
@ -76,7 +76,7 @@ impl Default for Align {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn align_rect(rect: Rect, align: (Align, Align)) -> Rect {
|
pub fn align_rect(rect: &Rect, align: (Align, Align)) -> Rect {
|
||||||
let x = match align.0 {
|
let x = match align.0 {
|
||||||
Align::Min => rect.left(),
|
Align::Min => rect.left(),
|
||||||
Align::Center => rect.left() - 0.5 * rect.width(),
|
Align::Center => rect.left() - 0.5 * rect.width(),
|
||||||
|
|
|
@ -20,6 +20,7 @@ pub mod math;
|
||||||
mod memory;
|
mod memory;
|
||||||
pub mod mesher;
|
pub mod mesher;
|
||||||
mod region;
|
mod region;
|
||||||
|
mod resize;
|
||||||
mod scroll_area;
|
mod scroll_area;
|
||||||
mod style;
|
mod style;
|
||||||
mod texture_atlas;
|
mod texture_atlas;
|
||||||
|
@ -40,6 +41,7 @@ pub use {
|
||||||
memory::Memory,
|
memory::Memory,
|
||||||
mesher::{Mesh, PaintBatches, Vertex},
|
mesher::{Mesh, PaintBatches, Vertex},
|
||||||
region::Region,
|
region::Region,
|
||||||
|
resize::Resize,
|
||||||
scroll_area::ScrollArea,
|
scroll_area::ScrollArea,
|
||||||
style::Style,
|
style::Style,
|
||||||
texture_atlas::Texture,
|
texture_atlas::Texture,
|
||||||
|
|
|
@ -11,6 +11,17 @@ pub fn vec2(x: f32, y: f32) -> Vec2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vec2 {
|
impl Vec2 {
|
||||||
|
pub fn zero() -> Self {
|
||||||
|
Self { x: 0.0, y: 0.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn infinity() -> Self {
|
||||||
|
Self {
|
||||||
|
x: f32::INFINITY,
|
||||||
|
y: f32::INFINITY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn splat(v: impl Into<f32>) -> Self {
|
pub fn splat(v: impl Into<f32>) -> Self {
|
||||||
let v: f32 = v.into();
|
let v: f32 = v.into();
|
||||||
Self { x: v, y: v }
|
Self { x: v, y: v }
|
||||||
|
@ -50,14 +61,17 @@ impl Vec2 {
|
||||||
vec2(angle.cos(), angle.sin())
|
vec2(angle.cos(), angle.sin())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn floor(self) -> Self {
|
pub fn floor(self) -> Self {
|
||||||
vec2(self.x.floor(), self.y.floor())
|
vec2(self.x.floor(), self.y.floor())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn round(self) -> Self {
|
pub fn round(self) -> Self {
|
||||||
vec2(self.x.round(), self.y.round())
|
vec2(self.x.round(), self.y.round())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn ceil(self) -> Self {
|
pub fn ceil(self) -> Self {
|
||||||
vec2(self.x.ceil(), self.y.ceil())
|
vec2(self.x.ceil(), self.y.ceil())
|
||||||
}
|
}
|
||||||
|
@ -66,13 +80,23 @@ impl Vec2 {
|
||||||
self.x.is_finite() && self.y.is_finite()
|
self.x.is_finite() && self.y.is_finite()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn min(self, other: Self) -> Self {
|
pub fn min(self, other: Self) -> Self {
|
||||||
vec2(self.x.min(other.x), self.y.min(other.y))
|
vec2(self.x.min(other.x), self.y.min(other.y))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn max(self, other: Self) -> Self {
|
pub fn max(self, other: Self) -> Self {
|
||||||
vec2(self.x.max(other.x), self.y.max(other.y))
|
vec2(self.x.max(other.x), self.y.max(other.y))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn clamp(self, range: RangeInclusive<Self>) -> Self {
|
||||||
|
Self {
|
||||||
|
x: clamp(self.x, range.start().x..=range.end().x),
|
||||||
|
y: clamp(self.y, range.start().y..=range.end().y),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Vec2 {
|
impl PartialEq for Vec2 {
|
||||||
|
@ -210,13 +234,23 @@ impl Pos2 {
|
||||||
self.x.is_finite() && self.y.is_finite()
|
self.x.is_finite() && self.y.is_finite()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn min(self, other: Self) -> Self {
|
pub fn min(self, other: Self) -> Self {
|
||||||
pos2(self.x.min(other.x), self.y.min(other.y))
|
pos2(self.x.min(other.x), self.y.min(other.y))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn max(self, other: Self) -> Self {
|
pub fn max(self, other: Self) -> Self {
|
||||||
pos2(self.x.max(other.x), self.y.max(other.y))
|
pos2(self.x.max(other.x), self.y.max(other.y))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn clamp(self, range: RangeInclusive<Self>) -> Self {
|
||||||
|
Self {
|
||||||
|
x: clamp(self.x, range.start().x..=range.end().x),
|
||||||
|
y: clamp(self.y, range.start().y..=range.end().y),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Pos2 {
|
impl PartialEq for Pos2 {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{collapsing_header, scroll_area, window, *};
|
use crate::{collapsing_header, resize, scroll_area, window, *};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Memory {
|
pub struct Memory {
|
||||||
|
@ -10,6 +10,7 @@ pub struct Memory {
|
||||||
// states of various types of widgets
|
// states of various types of widgets
|
||||||
pub(crate) collapsing_headers: HashMap<Id, collapsing_header::State>,
|
pub(crate) collapsing_headers: HashMap<Id, collapsing_header::State>,
|
||||||
pub(crate) scroll_areas: HashMap<Id, scroll_area::State>,
|
pub(crate) scroll_areas: HashMap<Id, scroll_area::State>,
|
||||||
|
pub(crate) resize: HashMap<Id, resize::State>,
|
||||||
windows: HashMap<Id, window::State>,
|
windows: HashMap<Id, window::State>,
|
||||||
|
|
||||||
/// Top is last
|
/// Top is last
|
||||||
|
|
|
@ -126,6 +126,10 @@ impl Region {
|
||||||
self.ctx.input()
|
self.ctx.input()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn memory(&self) -> parking_lot::MutexGuard<Memory> {
|
||||||
|
self.ctx.memory.lock()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fonts(&self) -> &Fonts {
|
pub fn fonts(&self) -> &Fonts {
|
||||||
&*self.ctx.fonts
|
&*self.ctx.fonts
|
||||||
}
|
}
|
||||||
|
@ -308,9 +312,13 @@ impl Region {
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Check for clicks on this entire region (desired_rect)
|
/// Check for clicks on this entire region (desired_rect)
|
||||||
pub fn interact(&self) -> InteractInfo {
|
pub fn interact_whole(&self) -> InteractInfo {
|
||||||
self.ctx
|
self.ctx
|
||||||
.interact(self.layer, self.desired_rect, Some(self.id))
|
.interact(self.layer, &self.desired_rect, Some(self.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interact_rect(&self, rect: &Rect, id: Id) -> InteractInfo {
|
||||||
|
self.ctx.interact(self.layer, rect, Some(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
@ -342,7 +350,7 @@ impl Region {
|
||||||
pub fn reserve_space(&mut self, size: Vec2, interaction_id: Option<Id>) -> InteractInfo {
|
pub fn reserve_space(&mut self, size: Vec2, interaction_id: Option<Id>) -> InteractInfo {
|
||||||
let pos = self.reserve_space_without_padding(size + self.style.item_spacing);
|
let pos = self.reserve_space_without_padding(size + self.style.item_spacing);
|
||||||
let rect = Rect::from_min_size(pos, size);
|
let rect = Rect::from_min_size(pos, size);
|
||||||
self.ctx.interact(self.layer, rect, interaction_id)
|
self.ctx.interact(self.layer, &rect, interaction_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reserve this much space and move the cursor.
|
/// Reserve this much space and move the cursor.
|
||||||
|
@ -394,6 +402,13 @@ impl Region {
|
||||||
self.id.with(id_seed)
|
self.id.with(id_seed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
/// Paint some debug text at current cursor
|
||||||
|
pub fn debug_text(&self, text: &str) {
|
||||||
|
self.ctx.debug_text(self.cursor, text);
|
||||||
|
}
|
||||||
|
|
||||||
/// Show some text anywhere in the region.
|
/// Show some text anywhere in the region.
|
||||||
/// To center the text at the given position, use `align: (Center, Center)`.
|
/// To center the text at the given position, use `align: (Center, Center)`.
|
||||||
/// If you want to draw text floating on top of everything,
|
/// If you want to draw text floating on top of everything,
|
||||||
|
@ -408,7 +423,7 @@ impl Region {
|
||||||
) -> Vec2 {
|
) -> Vec2 {
|
||||||
let font = &self.fonts()[text_style];
|
let font = &self.fonts()[text_style];
|
||||||
let (text, size) = font.layout_multiline(text, f32::INFINITY);
|
let (text, size) = font.layout_multiline(text, f32::INFINITY);
|
||||||
let rect = align_rect(Rect::from_min_size(pos, size), align);
|
let rect = align_rect(&Rect::from_min_size(pos, size), align);
|
||||||
self.add_text(rect.min(), text_style, text, text_color);
|
self.add_text(rect.min(), text_style, text, text_color);
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
|
|
146
emigui/src/resize.rs
Normal file
146
emigui/src/resize.rs
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
#![allow(unused_variables)] // TODO
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct State {
|
||||||
|
pub size: Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Resize {
|
||||||
|
// Will still try to stay within parent region bounds
|
||||||
|
min_size: Vec2,
|
||||||
|
max_size: Vec2,
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Resize {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
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,
|
||||||
|
expand_width_to_fit_content: true,
|
||||||
|
expand_height_to_fit_content: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resize {
|
||||||
|
pub fn default_height(mut self, height: f32) -> Self {
|
||||||
|
self.default_size.y = height;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_wide_as_possible(mut self) -> Self {
|
||||||
|
self.min_size.x = f32::INFINITY;
|
||||||
|
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)) {
|
||||||
|
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());
|
||||||
|
self.max_size = self.max_size.max(self.min_size);
|
||||||
|
|
||||||
|
let (is_new, mut state) = match region.memory().resize.get(&id) {
|
||||||
|
Some(state) => (false, state.clone()),
|
||||||
|
None => {
|
||||||
|
let default_size = self.default_size.clamp(self.min_size..=self.max_size);
|
||||||
|
(true, State { size: default_size })
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
state.size = state.size.clamp(self.min_size..=self.max_size);
|
||||||
|
|
||||||
|
let position = region.cursor();
|
||||||
|
|
||||||
|
// 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_interact = region.interact_rect(&corner_rect, id.with("corner"));
|
||||||
|
|
||||||
|
if corner_interact.active {
|
||||||
|
if let Some(mouse_pos) = region.input().mouse_pos {
|
||||||
|
state.size = mouse_pos - position + 0.5 * corner_interact.rect.size();
|
||||||
|
// We don't clamp to max size, because we want to be able to push against outer bounds.
|
||||||
|
// For instance, if we are inside a bigger Resize region, we want to expand that.
|
||||||
|
// state.size = state.size.clamp(self.min_size..=self.max_size);
|
||||||
|
state.size = state.size.max(self.min_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------
|
||||||
|
|
||||||
|
let inner_rect = Rect::from_min_size(region.cursor(), state.size);
|
||||||
|
let desired_size = {
|
||||||
|
let mut contents_region = region.child_region(inner_rect);
|
||||||
|
add_contents(&mut contents_region);
|
||||||
|
let desired_size = contents_region.bounding_size;
|
||||||
|
desired_size
|
||||||
|
};
|
||||||
|
let desired_size = desired_size.ceil(); // Avoid rounding errors in math
|
||||||
|
|
||||||
|
// ------------------------------
|
||||||
|
|
||||||
|
if self.shrink_width_to_fit_content {
|
||||||
|
state.size.x = state.size.x.min(desired_size.x);
|
||||||
|
}
|
||||||
|
if self.shrink_height_to_fit_content {
|
||||||
|
state.size.y = state.size.y.min(desired_size.y);
|
||||||
|
}
|
||||||
|
if self.expand_width_to_fit_content || is_new {
|
||||||
|
state.size.x = state.size.x.max(desired_size.x);
|
||||||
|
}
|
||||||
|
if self.expand_height_to_fit_content || is_new {
|
||||||
|
state.size.y = state.size.y.max(desired_size.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.size = state.size.max(self.min_size);
|
||||||
|
// state.size = state.size.clamp(self.min_size..=self.max_size);
|
||||||
|
state.size = state.size.round(); // TODO: round to pixels
|
||||||
|
|
||||||
|
region.reserve_space_without_padding(state.size);
|
||||||
|
|
||||||
|
// ------------------------------
|
||||||
|
|
||||||
|
paint_resize_corner(region, &corner_rect, &corner_interact);
|
||||||
|
|
||||||
|
if corner_interact.hovered || corner_interact.active {
|
||||||
|
region.ctx().output.lock().cursor_icon = CursorIcon::ResizeNwSe;
|
||||||
|
}
|
||||||
|
|
||||||
|
region.memory().resize.insert(id, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint_resize_corner(region: &mut Region, rect: &Rect, interact: &InteractInfo) {
|
||||||
|
let color = region.style().interact_stroke_color(&interact);
|
||||||
|
let width = region.style().interact_stroke_width(&interact);
|
||||||
|
|
||||||
|
let corner = rect.right_bottom().round(); // TODO: round to pixels
|
||||||
|
let mut w = 2.0;
|
||||||
|
|
||||||
|
while w < 12.0 {
|
||||||
|
region.add_paint_cmd(PaintCmd::line_segment(
|
||||||
|
(pos2(corner.x - w, corner.y), pos2(corner.x, corner.y - w)),
|
||||||
|
color,
|
||||||
|
width,
|
||||||
|
));
|
||||||
|
w += 4.0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,7 +57,7 @@ impl ScrollArea {
|
||||||
|
|
||||||
let content_interact = ctx.interact(
|
let content_interact = ctx.interact(
|
||||||
outer_region.layer,
|
outer_region.layer,
|
||||||
inner_rect,
|
&inner_rect,
|
||||||
Some(scroll_area_id.with("area")),
|
Some(scroll_area_id.with("area")),
|
||||||
);
|
);
|
||||||
if content_interact.active {
|
if content_interact.active {
|
||||||
|
@ -95,7 +95,7 @@ impl ScrollArea {
|
||||||
|
|
||||||
// intentionally use same id for inside and outside of handle
|
// intentionally use same id for inside and outside of handle
|
||||||
let interact_id = Some(scroll_area_id.with("vertical"));
|
let interact_id = Some(scroll_area_id.with("vertical"));
|
||||||
let handle_interact = ctx.interact(outer_region.layer, handle_rect, interact_id);
|
let handle_interact = ctx.interact(outer_region.layer, &handle_rect, interact_id);
|
||||||
|
|
||||||
if let Some(mouse_pos) = ctx.input.mouse_pos {
|
if let Some(mouse_pos) = ctx.input.mouse_pos {
|
||||||
if handle_interact.active {
|
if handle_interact.active {
|
||||||
|
@ -106,7 +106,7 @@ impl ScrollArea {
|
||||||
} else {
|
} else {
|
||||||
// Check for mouse down outside handle:
|
// Check for mouse down outside handle:
|
||||||
let scroll_bg_interact =
|
let scroll_bg_interact =
|
||||||
ctx.interact(outer_region.layer, outer_scroll_rect, interact_id);
|
ctx.interact(outer_region.layer, &outer_scroll_rect, interact_id);
|
||||||
|
|
||||||
if scroll_bg_interact.active {
|
if scroll_bg_interact.active {
|
||||||
// Center scroll at mouse pos:
|
// Center scroll at mouse pos:
|
||||||
|
|
|
@ -180,3 +180,13 @@ pub enum PaintCmd {
|
||||||
/// Low-level triangle mesh
|
/// Low-level triangle mesh
|
||||||
Mesh(Mesh),
|
Mesh(Mesh),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PaintCmd {
|
||||||
|
pub fn line_segment(seg: (Pos2, Pos2), color: Color, width: f32) -> Self {
|
||||||
|
Self::Line {
|
||||||
|
points: vec![seg.0, seg.1],
|
||||||
|
color,
|
||||||
|
width,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl Default for Window {
|
||||||
default_size: None,
|
default_size: None,
|
||||||
resizeable: true,
|
resizeable: true,
|
||||||
shrink_width_to_fit_content: false,
|
shrink_width_to_fit_content: false,
|
||||||
shrink_height_to_fit_content: true,
|
shrink_height_to_fit_content: false,
|
||||||
expand_width_to_fit_content: true,
|
expand_width_to_fit_content: true,
|
||||||
expand_height_to_fit_content: true,
|
expand_height_to_fit_content: true,
|
||||||
min_size: Vec2::splat(16.0),
|
min_size: Vec2::splat(16.0),
|
||||||
|
@ -121,7 +121,7 @@ impl Window {
|
||||||
|
|
||||||
let id = ctx.make_unique_id(&self.title, default_pos);
|
let id = ctx.make_unique_id(&self.title, default_pos);
|
||||||
|
|
||||||
let (mut state, is_new_window) = match ctx.memory.lock().get_window(id) {
|
let (mut state, is_new) = match ctx.memory.lock().get_window(id) {
|
||||||
Some(state) => (state, false),
|
Some(state) => (state, false),
|
||||||
None => {
|
None => {
|
||||||
let state = State {
|
let state = State {
|
||||||
|
@ -167,10 +167,10 @@ impl Window {
|
||||||
if self.shrink_height_to_fit_content {
|
if self.shrink_height_to_fit_content {
|
||||||
new_inner_size.y = new_inner_size.y.min(desired_inner_size.y);
|
new_inner_size.y = new_inner_size.y.min(desired_inner_size.y);
|
||||||
}
|
}
|
||||||
if self.expand_width_to_fit_content || is_new_window {
|
if self.expand_width_to_fit_content || is_new {
|
||||||
new_inner_size.x = new_inner_size.x.max(desired_inner_size.x);
|
new_inner_size.x = new_inner_size.x.max(desired_inner_size.x);
|
||||||
}
|
}
|
||||||
if self.expand_height_to_fit_content || is_new_window {
|
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.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.max(min_inner_size);
|
||||||
|
@ -201,7 +201,7 @@ impl Window {
|
||||||
let corner_center = outer_rect.max() - Vec2::splat(corner_radius);
|
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_rect = Rect::from_min_size(corner_center, Vec2::splat(corner_radius));
|
||||||
|
|
||||||
let corner_interact = ctx.interact(layer, corner_rect, Some(id.with("corner")));
|
let corner_interact = ctx.interact(layer, &corner_rect, Some(id.with("corner")));
|
||||||
|
|
||||||
graphics.layer(layer).push((
|
graphics.layer(layer).push((
|
||||||
Rect::everything(),
|
Rect::everything(),
|
||||||
|
@ -212,7 +212,7 @@ impl Window {
|
||||||
InteractInfo::default()
|
InteractInfo::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let win_interact = ctx.interact(layer, outer_rect, Some(id.with("window")));
|
let win_interact = ctx.interact(layer, &outer_rect, Some(id.with("window")));
|
||||||
|
|
||||||
if corner_interact.active {
|
if corner_interact.active {
|
||||||
if let Some(mouse_pos) = ctx.input().mouse_pos {
|
if let Some(mouse_pos) = ctx.input().mouse_pos {
|
||||||
|
|
|
@ -115,7 +115,7 @@ fn main() {
|
||||||
|
|
||||||
Window::new("Examples")
|
Window::new("Examples")
|
||||||
.default_pos(pos2(50.0, 100.0))
|
.default_pos(pos2(50.0, 100.0))
|
||||||
.default_size(vec2(300.0, 400.0))
|
.default_size(vec2(300.0, 600.0))
|
||||||
.show(region.ctx(), |region| {
|
.show(region.ctx(), |region| {
|
||||||
example_app.ui(region);
|
example_app.ui(region);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue