A `Window` can now be resizable in only one direction (#4155)
For instance: `Window::new(…).resizable([true, false])` is a window that is only resizable in the horizontal direction. This PR also removes a hack added in https://github.com/emilk/egui/pull/3039 which is no longer needed since https://github.com/emilk/egui/pull/4026
This commit is contained in:
parent
a93c6cd5d2
commit
00a399b2f7
|
|
@ -20,10 +20,6 @@ pub(crate) struct State {
|
|||
/// If false, clicks goes straight through to what is behind us.
|
||||
/// Good for tooltips etc.
|
||||
pub interactable: bool,
|
||||
|
||||
/// When `true`, this `Area` belongs to a resizable window, so it needs to
|
||||
/// receive mouse input which occurs a short distance beyond its bounding rect.
|
||||
pub edges_padded_for_resize: bool,
|
||||
}
|
||||
|
||||
impl State {
|
||||
|
|
@ -75,7 +71,6 @@ pub struct Area {
|
|||
pivot: Align2,
|
||||
anchor: Option<(Align2, Vec2)>,
|
||||
new_pos: Option<Pos2>,
|
||||
edges_padded_for_resize: bool,
|
||||
}
|
||||
|
||||
impl Area {
|
||||
|
|
@ -93,7 +88,6 @@ impl Area {
|
|||
new_pos: None,
|
||||
pivot: Align2::LEFT_TOP,
|
||||
anchor: None,
|
||||
edges_padded_for_resize: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -227,14 +221,6 @@ impl Area {
|
|||
Align2::LEFT_TOP
|
||||
}
|
||||
}
|
||||
|
||||
/// When `true`, this `Area` belongs to a resizable window, so it needs to
|
||||
/// receive mouse input which occurs a short distance beyond its bounding rect.
|
||||
#[inline]
|
||||
pub(crate) fn edges_padded_for_resize(mut self, edges_padded_for_resize: bool) -> Self {
|
||||
self.edges_padded_for_resize = edges_padded_for_resize;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Prepared {
|
||||
|
|
@ -279,7 +265,6 @@ impl Area {
|
|||
anchor,
|
||||
constrain,
|
||||
constrain_rect,
|
||||
edges_padded_for_resize,
|
||||
} = self;
|
||||
|
||||
let layer_id = LayerId::new(order, id);
|
||||
|
|
@ -300,11 +285,9 @@ impl Area {
|
|||
pivot,
|
||||
size: Vec2::ZERO,
|
||||
interactable,
|
||||
edges_padded_for_resize,
|
||||
});
|
||||
state.pivot_pos = new_pos.unwrap_or(state.pivot_pos);
|
||||
state.interactable = interactable;
|
||||
state.edges_padded_for_resize = edges_padded_for_resize;
|
||||
|
||||
if let Some((anchor, offset)) = anchor {
|
||||
let screen = ctx.available_rect();
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ pub struct Resize {
|
|||
id_source: Option<Id>,
|
||||
|
||||
/// If false, we are no enabled
|
||||
resizable: bool,
|
||||
resizable: Vec2b,
|
||||
|
||||
pub(crate) min_size: Vec2,
|
||||
pub(crate) max_size: Vec2,
|
||||
|
|
@ -49,7 +49,7 @@ impl Default for Resize {
|
|||
Self {
|
||||
id: None,
|
||||
id_source: None,
|
||||
resizable: true,
|
||||
resizable: Vec2b::TRUE,
|
||||
min_size: Vec2::splat(16.0),
|
||||
max_size: Vec2::splat(f32::INFINITY),
|
||||
default_size: vec2(320.0, 128.0), // TODO(emilk): preferred size of [`Resize`] area.
|
||||
|
|
@ -152,12 +152,13 @@ impl Resize {
|
|||
///
|
||||
/// Default is `true`.
|
||||
#[inline]
|
||||
pub fn resizable(mut self, resizable: bool) -> Self {
|
||||
self.resizable = resizable;
|
||||
pub fn resizable(mut self, resizable: impl Into<Vec2b>) -> Self {
|
||||
self.resizable = resizable.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_resizable(&self) -> bool {
|
||||
#[inline]
|
||||
pub fn is_resizable(&self) -> Vec2b {
|
||||
self.resizable
|
||||
}
|
||||
|
||||
|
|
@ -175,7 +176,7 @@ impl Resize {
|
|||
self.default_size = size;
|
||||
self.min_size = size;
|
||||
self.max_size = size;
|
||||
self.resizable = false;
|
||||
self.resizable = Vec2b::FALSE;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -226,7 +227,7 @@ impl Resize {
|
|||
|
||||
let mut user_requested_size = state.requested_size.take();
|
||||
|
||||
let corner_id = self.resizable.then(|| id.with("__resize_corner"));
|
||||
let corner_id = self.resizable.any().then(|| id.with("__resize_corner"));
|
||||
|
||||
if let Some(corner_id) = corner_id {
|
||||
if let Some(corner_response) = ui.ctx().read_response(corner_id) {
|
||||
|
|
@ -299,18 +300,21 @@ impl Resize {
|
|||
|
||||
// ------------------------------
|
||||
|
||||
let size = if self.with_stroke || self.resizable {
|
||||
// We show how large we are,
|
||||
// so we must follow the contents:
|
||||
let mut size = state.last_content_size;
|
||||
for d in 0..2 {
|
||||
if self.with_stroke || self.resizable[d] {
|
||||
// We show how large we are,
|
||||
// so we must follow the contents:
|
||||
|
||||
state.desired_size = state.desired_size.max(state.last_content_size);
|
||||
state.desired_size[d] = state.desired_size[d].max(state.last_content_size[d]);
|
||||
|
||||
// We are as large as we look
|
||||
state.desired_size
|
||||
} else {
|
||||
// Probably a window.
|
||||
state.last_content_size
|
||||
};
|
||||
// We are as large as we look
|
||||
size[d] = state.desired_size[d];
|
||||
} else {
|
||||
// Probably a window.
|
||||
size[d] = state.last_content_size[d];
|
||||
}
|
||||
}
|
||||
ui.advance_cursor_after_rect(Rect::from_min_size(content_ui.min_rect().min, size));
|
||||
|
||||
// ------------------------------
|
||||
|
|
|
|||
|
|
@ -48,9 +48,7 @@ impl<'open> Window<'open> {
|
|||
/// If you need a changing title, you must call `window.id(…)` with a fixed id.
|
||||
pub fn new(title: impl Into<WidgetText>) -> Self {
|
||||
let title = title.into().fallback_text_style(TextStyle::Heading);
|
||||
let area = Area::new(Id::new(title.text()))
|
||||
.constrain(true)
|
||||
.edges_padded_for_resize(true);
|
||||
let area = Area::new(Id::new(title.text())).constrain(true);
|
||||
Self {
|
||||
title,
|
||||
open: None,
|
||||
|
|
@ -119,9 +117,6 @@ impl<'open> Window<'open> {
|
|||
#[inline]
|
||||
pub fn resize(mut self, mutate: impl Fn(Resize) -> Resize) -> Self {
|
||||
self.resize = mutate(self.resize);
|
||||
self.area = self
|
||||
.area
|
||||
.edges_padded_for_resize(self.resize.is_resizable());
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -278,7 +273,6 @@ impl<'open> Window<'open> {
|
|||
#[inline]
|
||||
pub fn fixed_size(mut self, size: impl Into<Vec2>) -> Self {
|
||||
self.resize = self.resize.fixed_size(size);
|
||||
self.area = self.area.edges_padded_for_resize(false);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -296,11 +290,15 @@ impl<'open> Window<'open> {
|
|||
///
|
||||
/// Note that even if you set this to `false` the window may still auto-resize.
|
||||
///
|
||||
/// You can set the window to only be resizable in one direction by using
|
||||
/// e.g. `[true, false]` as the argument,
|
||||
/// making the window only resizable in the x-direction.
|
||||
///
|
||||
/// Default is `true`.
|
||||
#[inline]
|
||||
pub fn resizable(mut self, resizable: bool) -> Self {
|
||||
pub fn resizable(mut self, resizable: impl Into<Vec2b>) -> Self {
|
||||
let resizable = resizable.into();
|
||||
self.resize = self.resize.resizable(resizable);
|
||||
self.area = self.area.edges_padded_for_resize(resizable);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -326,7 +324,6 @@ impl<'open> Window<'open> {
|
|||
pub fn auto_sized(mut self) -> Self {
|
||||
self.resize = self.resize.auto_sized();
|
||||
self.scroll = ScrollArea::neither();
|
||||
self.area = self.area.edges_padded_for_resize(false);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -589,7 +586,20 @@ fn paint_resize_corner(
|
|||
} else if possible.resize_right && possible.resize_top {
|
||||
(Align2::RIGHT_TOP, rounding.ne)
|
||||
} else {
|
||||
return;
|
||||
// We're not in two directions, but it is still nice to tell the user
|
||||
// we're resizable by painting the resize corner in the expected place
|
||||
// (i.e. for windows only resizable in one direction):
|
||||
if possible.resize_right || possible.resize_bottom {
|
||||
(Align2::RIGHT_BOTTOM, rounding.se)
|
||||
} else if possible.resize_left || possible.resize_bottom {
|
||||
(Align2::LEFT_BOTTOM, rounding.sw)
|
||||
} else if possible.resize_left || possible.resize_top {
|
||||
(Align2::LEFT_TOP, rounding.nw)
|
||||
} else if possible.resize_right || possible.resize_top {
|
||||
(Align2::RIGHT_TOP, rounding.ne)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Adjust the corner offset to accommodate the stroke width and window rounding
|
||||
|
|
@ -621,13 +631,15 @@ struct PossibleInteractions {
|
|||
impl PossibleInteractions {
|
||||
fn new(area: &Area, resize: &Resize, is_collapsed: bool) -> Self {
|
||||
let movable = area.is_enabled() && area.is_movable();
|
||||
let resizable = area.is_enabled() && resize.is_resizable() && !is_collapsed;
|
||||
let resizable = resize
|
||||
.is_resizable()
|
||||
.and(area.is_enabled() && !is_collapsed);
|
||||
let pivot = area.get_pivot();
|
||||
Self {
|
||||
resize_left: resizable && (movable || pivot.x() != Align::LEFT),
|
||||
resize_right: resizable && (movable || pivot.x() != Align::RIGHT),
|
||||
resize_top: resizable && (movable || pivot.y() != Align::TOP),
|
||||
resize_bottom: resizable && (movable || pivot.y() != Align::BOTTOM),
|
||||
resize_left: resizable.x && (movable || pivot.x() != Align::LEFT),
|
||||
resize_right: resizable.x && (movable || pivot.x() != Align::RIGHT),
|
||||
resize_top: resizable.y && (movable || pivot.y() != Align::TOP),
|
||||
resize_bottom: resizable.y && (movable || pivot.y() != Align::BOTTOM),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -496,7 +496,6 @@ impl ContextImpl {
|
|||
pivot: Align2::LEFT_TOP,
|
||||
size: screen_rect.size(),
|
||||
interactable: true,
|
||||
edges_padded_for_resize: false,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -2331,9 +2330,7 @@ impl Context {
|
|||
|
||||
/// Top-most layer at the given position.
|
||||
pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {
|
||||
self.memory(|mem| {
|
||||
mem.layer_id_at(pos, mem.options.style.interaction.resize_grab_radius_side)
|
||||
})
|
||||
self.memory(|mem| mem.layer_id_at(pos))
|
||||
}
|
||||
|
||||
/// Moves the given area to the top in its [`Order`].
|
||||
|
|
|
|||
|
|
@ -646,9 +646,8 @@ impl Memory {
|
|||
}
|
||||
|
||||
/// Top-most layer at the given position.
|
||||
pub fn layer_id_at(&self, pos: Pos2, resize_interact_radius_side: f32) -> Option<LayerId> {
|
||||
self.areas()
|
||||
.layer_id_at(pos, resize_interact_radius_side, &self.layer_transforms)
|
||||
pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {
|
||||
self.areas().layer_id_at(pos, &self.layer_transforms)
|
||||
}
|
||||
|
||||
/// An iterator over all layers. Back-to-front. Top is last.
|
||||
|
|
@ -921,7 +920,6 @@ impl Areas {
|
|||
pub fn layer_id_at(
|
||||
&self,
|
||||
pos: Pos2,
|
||||
resize_interact_radius_side: f32,
|
||||
layer_transforms: &HashMap<LayerId, TSTransform>,
|
||||
) -> Option<LayerId> {
|
||||
for layer in self.order.iter().rev() {
|
||||
|
|
@ -929,11 +927,6 @@ impl Areas {
|
|||
if let Some(state) = self.areas.get(&layer.id) {
|
||||
let mut rect = state.rect();
|
||||
if state.interactable {
|
||||
if state.edges_padded_for_resize {
|
||||
// Allow us to resize by dragging just outside the window:
|
||||
rect = rect.expand(resize_interact_radius_side);
|
||||
}
|
||||
|
||||
if let Some(transform) = layer_transforms.get(layer) {
|
||||
rect = *transform * rect;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ impl super::Demo for About {
|
|||
.default_width(320.0)
|
||||
.default_height(480.0)
|
||||
.open(open)
|
||||
.resizable([true, false])
|
||||
.show(ctx, |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ impl super::Demo for CodeExample {
|
|||
.default_size([800.0, 400.0])
|
||||
.vscroll(false)
|
||||
.hscroll(true)
|
||||
.resizable([true, false])
|
||||
.show(ctx, |ui| self.ui(ui));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ impl super::Demo for WidgetGallery {
|
|||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(true)
|
||||
.resizable([true, false])
|
||||
.default_width(280.0)
|
||||
.show(ctx, |ui| {
|
||||
use super::View as _;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
|
||||
use crate::Vec2b;
|
||||
|
||||
/// A vector has a direction and length.
|
||||
/// A [`Vec2`] is often used to represent a size.
|
||||
///
|
||||
|
|
@ -86,6 +88,16 @@ impl From<&Vec2> for (f32, f32) {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Vec2b> for Vec2 {
|
||||
#[inline(always)]
|
||||
fn from(v: Vec2b) -> Self {
|
||||
Self {
|
||||
x: v.x as i32 as f32,
|
||||
y: v.y as i32 as f32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Mint compatibility and convenience conversions
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,30 @@ impl Vec2b {
|
|||
pub fn any(&self) -> bool {
|
||||
self.x || self.y
|
||||
}
|
||||
|
||||
/// Are both `x` and `y` true?
|
||||
#[inline]
|
||||
pub fn all(&self) -> bool {
|
||||
self.x && self.y
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn and(&self, other: impl Into<Self>) -> Self {
|
||||
let other = other.into();
|
||||
Self {
|
||||
x: self.x && other.x,
|
||||
y: self.y && other.y,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn or(&self, other: impl Into<Self>) -> Self {
|
||||
let other = other.into();
|
||||
Self {
|
||||
x: self.x || other.x,
|
||||
y: self.y || other.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Vec2b {
|
||||
|
|
|
|||
Loading…
Reference in New Issue