2020-04-25 12:37:39 +00:00
|
|
|
use crate::*;
|
|
|
|
|
2020-05-30 09:04:40 +00:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
2021-01-10 10:37:47 +00:00
|
|
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
2020-05-02 09:37:12 +00:00
|
|
|
pub(crate) struct State {
|
2020-05-30 15:51:01 +00:00
|
|
|
/// This is the size that the user has picked by dragging the resize handles.
|
|
|
|
/// This may be smaller and/or larger than the actual size.
|
|
|
|
/// For instance, the user may have tried to shrink too much (not fitting the contents).
|
|
|
|
/// Or the user requested a large area, but the content don't need that much space.
|
|
|
|
pub(crate) desired_size: Vec2,
|
|
|
|
|
|
|
|
/// Actual size of content last frame
|
|
|
|
last_content_size: Vec2,
|
2020-05-17 14:42:20 +00:00
|
|
|
|
|
|
|
/// Externally requested size (e.g. by Window) for the next frame
|
|
|
|
pub(crate) requested_size: Option<Vec2>,
|
2020-04-25 12:37:39 +00:00
|
|
|
}
|
|
|
|
|
2020-08-09 15:24:32 +00:00
|
|
|
/// A region that can be resized by dragging the bottom right corner.
|
2020-04-25 12:37:39 +00:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
pub struct Resize {
|
2020-05-17 14:42:20 +00:00
|
|
|
id: Option<Id>,
|
2020-12-10 09:28:30 +00:00
|
|
|
id_source: Option<Id>,
|
2020-05-17 14:42:20 +00:00
|
|
|
|
2020-04-25 20:49:57 +00:00
|
|
|
/// If false, we are no enabled
|
|
|
|
resizable: bool,
|
|
|
|
|
2020-08-28 14:07:20 +00:00
|
|
|
pub(crate) min_size: Vec2,
|
2020-09-13 20:07:55 +00:00
|
|
|
pub(crate) max_size: Vec2,
|
2020-04-25 12:37:39 +00:00
|
|
|
|
|
|
|
default_size: Vec2,
|
|
|
|
|
2020-09-01 21:54:21 +00:00
|
|
|
with_stroke: bool,
|
2020-04-25 12:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Resize {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
2020-05-17 14:42:20 +00:00
|
|
|
id: None,
|
2020-12-10 09:28:30 +00:00
|
|
|
id_source: None,
|
2020-04-25 20:49:57 +00:00
|
|
|
resizable: true,
|
2020-07-18 22:17:02 +00:00
|
|
|
min_size: Vec2::splat(16.0),
|
2020-09-13 20:07:55 +00:00
|
|
|
max_size: Vec2::splat(f32::INFINITY),
|
2020-08-28 14:13:22 +00:00
|
|
|
default_size: vec2(320.0, 128.0), // TODO: preferred size of `Resize` area.
|
2020-09-01 21:54:21 +00:00
|
|
|
with_stroke: true,
|
2020-04-25 12:37:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Resize {
|
2020-08-26 20:54:57 +00:00
|
|
|
/// Assign an explicit and globally unique id.
|
2020-05-17 14:42:20 +00:00
|
|
|
pub fn id(mut self, id: Id) -> Self {
|
|
|
|
self.id = Some(id);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-12-10 09:28:30 +00:00
|
|
|
/// A source for the unique `Id`, e.g. `.id_source("second_resize_area")` or `.id_source(loop_index)`.
|
|
|
|
pub fn id_source(mut self, id_source: impl std::hash::Hash) -> Self {
|
|
|
|
self.id_source = Some(Id::new(id_source));
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-06-03 08:59:02 +00:00
|
|
|
/// Preferred / suggested width. Actual width will depend on contents.
|
|
|
|
///
|
|
|
|
/// Examples:
|
|
|
|
/// * if the contents is text, this will decide where we break long lines.
|
|
|
|
/// * if the contents is a canvas, this decides the width of it,
|
|
|
|
/// * if the contents is some buttons, this is ignored and we will auto-size.
|
2020-05-16 15:28:15 +00:00
|
|
|
pub fn default_width(mut self, width: f32) -> Self {
|
|
|
|
self.default_size.x = width;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-06-03 08:59:02 +00:00
|
|
|
/// Preferred / suggested height. Actual height will depend on contents.
|
|
|
|
///
|
|
|
|
/// Examples:
|
|
|
|
/// * if the contents is a `ScrollArea` then this decides the maximum size.
|
|
|
|
/// * if the contents is a canvas, this decides the height of it,
|
|
|
|
/// * if the contents is text and buttons, then the `default_height` is ignored
|
|
|
|
/// and the height is picked automatically..
|
2020-04-25 12:37:39 +00:00
|
|
|
pub fn default_height(mut self, height: f32) -> Self {
|
|
|
|
self.default_size.y = height;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-05-30 09:48:33 +00:00
|
|
|
pub fn default_size(mut self, default_size: impl Into<Vec2>) -> Self {
|
|
|
|
self.default_size = default_size.into();
|
2020-04-25 13:26:24 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-06-03 19:14:47 +00:00
|
|
|
/// Won't shrink to smaller than this
|
2020-07-18 22:17:02 +00:00
|
|
|
pub fn min_size(mut self, min_size: impl Into<Vec2>) -> Self {
|
|
|
|
self.min_size = min_size.into();
|
2020-04-25 20:49:57 +00:00
|
|
|
self
|
|
|
|
}
|
2020-10-01 20:25:44 +00:00
|
|
|
/// Won't shrink to smaller than this
|
|
|
|
pub fn min_width(mut self, min_width: f32) -> Self {
|
|
|
|
self.min_size.x = min_width;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
/// Won't shrink to smaller than this
|
|
|
|
pub fn min_height(mut self, min_height: f32) -> Self {
|
|
|
|
self.min_size.y = min_height;
|
|
|
|
self
|
|
|
|
}
|
2020-04-25 20:49:57 +00:00
|
|
|
|
2020-09-13 20:07:55 +00:00
|
|
|
/// Won't expand to larger than this
|
|
|
|
pub fn max_size(mut self, max_size: impl Into<Vec2>) -> Self {
|
|
|
|
self.max_size = max_size.into();
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-04-25 20:49:57 +00:00
|
|
|
/// 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
|
|
|
|
}
|
|
|
|
|
2020-05-17 14:42:20 +00:00
|
|
|
pub fn is_resizable(&self) -> bool {
|
|
|
|
self.resizable
|
|
|
|
}
|
|
|
|
|
2020-05-30 15:51:01 +00:00
|
|
|
/// Not manually resizable, just takes the size of its contents.
|
2020-09-13 20:07:55 +00:00
|
|
|
/// Text will not wrap, but will instead make your window width expand.
|
2020-05-10 12:27:02 +00:00
|
|
|
pub fn auto_sized(self) -> Self {
|
2020-07-18 22:17:02 +00:00
|
|
|
self.min_size(Vec2::zero())
|
2020-07-18 22:16:04 +00:00
|
|
|
.default_size(Vec2::splat(f32::INFINITY))
|
2020-05-10 17:00:48 +00:00
|
|
|
.resizable(false)
|
2020-05-10 12:27:02 +00:00
|
|
|
}
|
|
|
|
|
2020-05-30 09:48:33 +00:00
|
|
|
pub fn fixed_size(mut self, size: impl Into<Vec2>) -> Self {
|
|
|
|
let size = size.into();
|
2020-04-25 20:49:57 +00:00
|
|
|
self.default_size = size;
|
2020-07-18 22:17:02 +00:00
|
|
|
self.min_size = size;
|
2020-09-13 20:07:55 +00:00
|
|
|
self.max_size = size;
|
2020-04-25 20:49:57 +00:00
|
|
|
self.resizable = false;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-09-01 21:54:21 +00:00
|
|
|
pub fn with_stroke(mut self, with_stroke: bool) -> Self {
|
|
|
|
self.with_stroke = with_stroke;
|
2020-05-17 07:44:09 +00:00
|
|
|
self
|
|
|
|
}
|
2020-04-25 12:37:39 +00:00
|
|
|
}
|
|
|
|
|
2020-05-20 16:39:35 +00:00
|
|
|
struct Prepared {
|
|
|
|
id: Id,
|
|
|
|
state: State,
|
2020-08-30 06:52:42 +00:00
|
|
|
corner_response: Option<Response>,
|
2020-05-20 16:39:35 +00:00
|
|
|
content_ui: Ui,
|
|
|
|
}
|
|
|
|
|
2020-04-25 12:37:39 +00:00
|
|
|
impl Resize {
|
2020-05-23 09:28:21 +00:00
|
|
|
fn begin(&mut self, ui: &mut Ui) -> Prepared {
|
2020-12-10 22:23:03 +00:00
|
|
|
let position = ui.available_rect_before_wrap().min;
|
2020-12-10 09:28:30 +00:00
|
|
|
let id = self.id.unwrap_or_else(|| {
|
|
|
|
let id_source = self.id_source.unwrap_or_else(|| Id::new("resize"));
|
|
|
|
ui.make_persistent_id(id_source)
|
|
|
|
});
|
2020-04-25 12:37:39 +00:00
|
|
|
|
2020-05-30 15:51:01 +00:00
|
|
|
let mut state = ui.memory().resize.get(&id).cloned().unwrap_or_else(|| {
|
2020-08-28 14:41:37 +00:00
|
|
|
ui.ctx().request_repaint(); // counter frame delay
|
|
|
|
|
2020-09-16 19:39:45 +00:00
|
|
|
let default_size = self
|
|
|
|
.default_size
|
|
|
|
.at_least(self.min_size)
|
2020-12-16 19:31:00 +00:00
|
|
|
.at_most(self.max_size)
|
|
|
|
.at_most(
|
2021-02-01 15:55:40 +00:00
|
|
|
ui.input().screen_rect().size() - 2.0 * ui.spacing().window_padding, // hack for windows
|
2020-12-16 19:31:00 +00:00
|
|
|
);
|
2020-05-30 15:51:01 +00:00
|
|
|
|
|
|
|
State {
|
|
|
|
desired_size: default_size,
|
|
|
|
last_content_size: vec2(0.0, 0.0),
|
|
|
|
requested_size: None,
|
2020-04-25 12:37:39 +00:00
|
|
|
}
|
2020-05-30 15:51:01 +00:00
|
|
|
});
|
2020-04-25 12:37:39 +00:00
|
|
|
|
2020-09-16 19:39:45 +00:00
|
|
|
state.desired_size = state
|
|
|
|
.desired_size
|
|
|
|
.at_least(self.min_size)
|
|
|
|
.at_most(self.max_size);
|
2020-04-25 12:37:39 +00:00
|
|
|
|
2020-12-25 14:41:18 +00:00
|
|
|
let mut user_requested_size = state.requested_size.take();
|
|
|
|
|
2020-08-30 06:52:42 +00:00
|
|
|
let corner_response = if self.resizable {
|
2020-05-10 11:04:45 +00:00
|
|
|
// Resize-corner:
|
2020-08-31 20:27:31 +00:00
|
|
|
let corner_size = Vec2::splat(ui.style().visuals.resize_corner_size);
|
2020-08-26 20:10:42 +00:00
|
|
|
let corner_rect =
|
|
|
|
Rect::from_min_size(position + state.desired_size - corner_size, corner_size);
|
2020-08-30 06:52:42 +00:00
|
|
|
let corner_response = ui.interact(corner_rect, id.with("corner"), Sense::drag());
|
2020-05-10 11:04:45 +00:00
|
|
|
|
2021-01-25 17:50:19 +00:00
|
|
|
if let Some(pointer_pos) = corner_response.interact_pointer_pos() {
|
|
|
|
user_requested_size =
|
|
|
|
Some(pointer_pos - position + 0.5 * corner_response.rect.size());
|
2020-04-25 12:37:39 +00:00
|
|
|
}
|
2021-01-25 17:50:19 +00:00
|
|
|
|
2020-08-30 06:52:42 +00:00
|
|
|
Some(corner_response)
|
2020-05-10 11:04:45 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
2020-04-25 12:37:39 +00:00
|
|
|
|
2020-12-25 14:41:18 +00:00
|
|
|
if let Some(user_requested_size) = user_requested_size {
|
|
|
|
state.desired_size = user_requested_size;
|
|
|
|
} else {
|
|
|
|
// We are not being actively resized, so auto-expand to include size of last frame.
|
|
|
|
// This prevents auto-shrinking if the contents contain width-filling widgets (separators etc)
|
|
|
|
// but it makes a lot of interactions with `Window`s nicer.
|
|
|
|
state.desired_size = state.desired_size.max(state.last_content_size);
|
2020-05-17 14:42:20 +00:00
|
|
|
}
|
2020-12-25 14:41:18 +00:00
|
|
|
|
2020-09-16 19:39:45 +00:00
|
|
|
state.desired_size = state
|
|
|
|
.desired_size
|
|
|
|
.at_least(self.min_size)
|
|
|
|
.at_most(self.max_size);
|
2020-05-17 14:42:20 +00:00
|
|
|
|
2020-04-25 12:37:39 +00:00
|
|
|
// ------------------------------
|
|
|
|
|
2020-05-30 15:51:01 +00:00
|
|
|
let inner_rect = Rect::from_min_size(position, state.desired_size);
|
2020-05-19 21:57:48 +00:00
|
|
|
|
2020-08-31 20:27:31 +00:00
|
|
|
let mut content_clip_rect = inner_rect.expand(ui.style().visuals.clip_rect_margin);
|
2020-05-20 16:39:35 +00:00
|
|
|
|
2020-08-26 20:54:57 +00:00
|
|
|
// If we pull the resize handle to shrink, we want to TRY to shrink it.
|
2020-05-20 16:39:35 +00:00
|
|
|
// After laying out the contents, we might be much bigger.
|
|
|
|
// In those cases we don't want the clip_rect to be smaller, because
|
|
|
|
// then we will clip the contents of the region even thought the result gets larger. This is simply ugly!
|
2020-05-30 15:51:01 +00:00
|
|
|
// So we use the memory of last_content_size to make the clip rect large enough.
|
|
|
|
content_clip_rect.max = content_clip_rect.max.max(
|
2020-08-31 20:27:31 +00:00
|
|
|
inner_rect.min
|
|
|
|
+ state.last_content_size
|
|
|
|
+ Vec2::splat(ui.style().visuals.clip_rect_margin),
|
2020-05-30 15:51:01 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
content_clip_rect = content_clip_rect.intersect(ui.clip_rect()); // Respect parent region
|
2020-05-20 16:39:35 +00:00
|
|
|
|
2020-09-18 15:54:57 +00:00
|
|
|
let mut content_ui = ui.child_ui(inner_rect, *ui.layout());
|
2020-05-20 16:39:35 +00:00
|
|
|
content_ui.set_clip_rect(content_clip_rect);
|
|
|
|
|
|
|
|
Prepared {
|
|
|
|
id,
|
|
|
|
state,
|
2020-08-30 06:52:42 +00:00
|
|
|
corner_response,
|
2020-05-20 16:39:35 +00:00
|
|
|
content_ui,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn show<R>(mut self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> R {
|
2020-05-23 09:28:21 +00:00
|
|
|
let mut prepared = self.begin(ui);
|
2020-05-20 16:39:35 +00:00
|
|
|
let ret = add_contents(&mut prepared.content_ui);
|
2020-05-23 09:28:21 +00:00
|
|
|
self.end(ui, prepared);
|
2020-05-20 16:39:35 +00:00
|
|
|
ret
|
|
|
|
}
|
|
|
|
|
2020-05-23 09:28:21 +00:00
|
|
|
fn end(self, ui: &mut Ui, prepared: Prepared) {
|
2020-05-20 16:39:35 +00:00
|
|
|
let Prepared {
|
|
|
|
id,
|
|
|
|
mut state,
|
2020-08-30 06:52:42 +00:00
|
|
|
corner_response,
|
2020-05-20 16:39:35 +00:00
|
|
|
content_ui,
|
|
|
|
} = prepared;
|
|
|
|
|
2020-09-27 09:04:12 +00:00
|
|
|
state.last_content_size = content_ui.min_size();
|
2020-04-25 12:37:39 +00:00
|
|
|
|
|
|
|
// ------------------------------
|
|
|
|
|
2020-12-26 18:14:13 +00:00
|
|
|
let size = if self.with_stroke || self.resizable {
|
2020-05-30 15:51:01 +00:00
|
|
|
// We show how large we are,
|
|
|
|
// so we must follow the contents:
|
2020-04-25 12:37:39 +00:00
|
|
|
|
2020-05-30 15:51:01 +00:00
|
|
|
state.desired_size = state.desired_size.max(state.last_content_size);
|
2020-04-25 12:37:39 +00:00
|
|
|
|
2020-05-30 15:51:01 +00:00
|
|
|
// We are as large as we look
|
2020-12-26 18:14:13 +00:00
|
|
|
state.desired_size
|
2020-05-30 15:51:01 +00:00
|
|
|
} else {
|
|
|
|
// Probably a window.
|
2020-12-26 18:14:13 +00:00
|
|
|
state.last_content_size
|
|
|
|
};
|
|
|
|
ui.advance_cursor_after_rect(Rect::from_min_size(content_ui.min_rect().min, size));
|
2020-04-25 12:37:39 +00:00
|
|
|
|
|
|
|
// ------------------------------
|
|
|
|
|
2020-09-01 21:54:21 +00:00
|
|
|
if self.with_stroke && corner_response.is_some() {
|
2020-10-08 20:24:27 +00:00
|
|
|
let rect = Rect::from_min_size(content_ui.min_rect().left_top(), state.desired_size);
|
2020-05-17 07:44:09 +00:00
|
|
|
let rect = rect.expand(2.0); // breathing room for content
|
2021-01-10 10:43:01 +00:00
|
|
|
ui.painter().add(paint::Shape::Rect {
|
2020-05-17 07:44:09 +00:00
|
|
|
rect,
|
|
|
|
corner_radius: 3.0,
|
2020-08-31 20:56:24 +00:00
|
|
|
fill: Default::default(),
|
2020-09-08 07:07:08 +00:00
|
|
|
stroke: ui.style().visuals.widgets.noninteractive.bg_stroke,
|
2020-05-17 07:44:09 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-08-30 06:52:42 +00:00
|
|
|
if let Some(corner_response) = corner_response {
|
|
|
|
paint_resize_corner(ui, &corner_response);
|
2020-04-25 12:37:39 +00:00
|
|
|
|
2021-01-25 17:50:19 +00:00
|
|
|
if corner_response.hovered() || corner_response.dragged() {
|
2020-05-10 11:04:45 +00:00
|
|
|
ui.ctx().output().cursor_icon = CursorIcon::ResizeNwSe;
|
|
|
|
}
|
2020-04-25 12:37:39 +00:00
|
|
|
}
|
|
|
|
|
2020-05-08 20:42:31 +00:00
|
|
|
ui.memory().resize.insert(id, state);
|
2020-06-03 19:14:47 +00:00
|
|
|
|
2020-08-31 20:27:31 +00:00
|
|
|
if ui.ctx().style().visuals.debug_resize {
|
2020-07-30 12:11:09 +00:00
|
|
|
ui.ctx().debug_painter().debug_rect(
|
2020-10-08 20:24:27 +00:00
|
|
|
Rect::from_min_size(content_ui.min_rect().left_top(), state.desired_size),
|
2021-01-02 16:18:41 +00:00
|
|
|
Color32::GREEN,
|
2020-06-03 19:14:47 +00:00
|
|
|
"desired_size",
|
|
|
|
);
|
2020-07-30 12:11:09 +00:00
|
|
|
ui.ctx().debug_painter().debug_rect(
|
2020-10-08 20:24:27 +00:00
|
|
|
Rect::from_min_size(content_ui.min_rect().left_top(), state.last_content_size),
|
2021-01-02 16:18:41 +00:00
|
|
|
Color32::LIGHT_BLUE,
|
2020-06-03 19:14:47 +00:00
|
|
|
"last_content_size",
|
|
|
|
);
|
|
|
|
}
|
2020-04-25 12:37:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-01 21:54:21 +00:00
|
|
|
use crate::paint::Stroke;
|
2020-08-26 18:40:25 +00:00
|
|
|
|
2020-08-30 06:52:42 +00:00
|
|
|
pub fn paint_resize_corner(ui: &mut Ui, response: &Response) {
|
2020-09-05 11:30:04 +00:00
|
|
|
let stroke = ui.style().interact(response).fg_stroke;
|
2020-09-01 23:36:52 +00:00
|
|
|
paint_resize_corner_with_style(ui, &response.rect, stroke);
|
2020-08-26 18:40:25 +00:00
|
|
|
}
|
2020-04-25 12:37:39 +00:00
|
|
|
|
2020-09-01 21:54:21 +00:00
|
|
|
pub fn paint_resize_corner_with_style(ui: &mut Ui, rect: &Rect, stroke: Stroke) {
|
2020-07-30 12:11:09 +00:00
|
|
|
let painter = ui.painter();
|
2020-08-26 18:40:25 +00:00
|
|
|
let corner = painter.round_pos_to_pixels(rect.right_bottom());
|
2020-04-25 12:37:39 +00:00
|
|
|
let mut w = 2.0;
|
|
|
|
|
2020-09-01 18:40:54 +00:00
|
|
|
while w <= rect.width() && w <= rect.height() {
|
2020-08-29 15:30:06 +00:00
|
|
|
painter.line_segment(
|
|
|
|
[pos2(corner.x - w, corner.y), pos2(corner.x, corner.y - w)],
|
2020-09-01 21:54:21 +00:00
|
|
|
stroke,
|
2020-08-29 15:30:06 +00:00
|
|
|
);
|
2020-04-25 12:37:39 +00:00
|
|
|
w += 4.0;
|
|
|
|
}
|
|
|
|
}
|