Fix Window::pivot causing windows to move around (#2694)

* Fix Window::pivot causing windows to move around

* Add line to changelog
This commit is contained in:
Emil Ernerfeldt 2023-02-08 12:41:36 +01:00 committed by GitHub
parent a8d5a82a7f
commit e8b9e706ca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 27 deletions

View file

@ -43,6 +43,7 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG
* The `button_padding` style option works closer as expected with image+text buttons now ([#2510](https://github.com/emilk/egui/pull/2510)). * The `button_padding` style option works closer as expected with image+text buttons now ([#2510](https://github.com/emilk/egui/pull/2510)).
* Fixed rendering of `…` (ellipsis). * Fixed rendering of `…` (ellipsis).
* Menus are now moved to fit on the screen. * Menus are now moved to fit on the screen.
* Fix `Window::pivot` causing windows to move around ([#2694](https://github.com/emilk/egui/pull/2694)).
## 0.20.1 - 2022-12-11 - Fix key-repeat ## 0.20.1 - 2022-12-11 - Fix key-repeat

View file

@ -9,8 +9,10 @@ use crate::*;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub(crate) struct State { pub(crate) struct State {
/// Last known pos /// Last known pos of the pivot
pub pos: Pos2, pub pivot_pos: Pos2,
pub pivot: Align2,
/// Last know size. Used for catching clicks. /// Last know size. Used for catching clicks.
pub size: Vec2, pub size: Vec2,
@ -21,8 +23,22 @@ pub(crate) struct State {
} }
impl State { impl State {
pub fn left_top_pos(&self) -> Pos2 {
pos2(
self.pivot_pos.x - self.pivot.x().to_factor() * self.size.x,
self.pivot_pos.y - self.pivot.y().to_factor() * self.size.y,
)
}
pub fn set_left_top_pos(&mut self, pos: Pos2) {
self.pivot_pos = pos2(
pos.x + self.pivot.x().to_factor() * self.size.x,
pos.y + self.pivot.y().to_factor() * self.size.y,
);
}
pub fn rect(&self) -> Rect { pub fn rect(&self) -> Rect {
Rect::from_min_size(self.pos, self.size) Rect::from_min_size(self.left_top_pos(), self.size)
} }
} }
@ -237,21 +253,19 @@ impl Area {
ctx.request_repaint(); // if we don't know the previous size we are likely drawing the area in the wrong place ctx.request_repaint(); // if we don't know the previous size we are likely drawing the area in the wrong place
} }
let mut state = state.unwrap_or_else(|| State { let mut state = state.unwrap_or_else(|| State {
pos: default_pos.unwrap_or_else(|| automatic_area_position(ctx)), pivot_pos: default_pos.unwrap_or_else(|| automatic_area_position(ctx)),
pivot,
size: Vec2::ZERO, size: Vec2::ZERO,
interactable, interactable,
}); });
state.pos = new_pos.unwrap_or(state.pos); state.pivot_pos = new_pos.unwrap_or(state.pivot_pos);
state.interactable = interactable; state.interactable = interactable;
if pivot != Align2::LEFT_TOP {
state.pos.x -= pivot.x().to_factor() * state.size.x;
state.pos.y -= pivot.y().to_factor() * state.size.y;
}
if let Some((anchor, offset)) = anchor { if let Some((anchor, offset)) = anchor {
let screen = ctx.available_rect(); let screen = ctx.available_rect();
state.pos = anchor.align_size_within_rect(state.size, screen).min + offset; state.set_left_top_pos(
anchor.align_size_within_rect(state.size, screen).left_top() + offset,
);
} }
// interact right away to prevent frame-delay // interact right away to prevent frame-delay
@ -278,12 +292,13 @@ impl Area {
// Important check - don't try to move e.g. a combobox popup! // Important check - don't try to move e.g. a combobox popup!
if movable { if movable {
if move_response.dragged() { if move_response.dragged() {
state.pos += ctx.input(|i| i.pointer.delta()); state.pivot_pos += ctx.input(|i| i.pointer.delta());
} }
state.pos = ctx state.set_left_top_pos(
.constrain_window_rect_to_area(state.rect(), drag_bounds) ctx.constrain_window_rect_to_area(state.rect(), drag_bounds)
.min; .min,
);
} }
if (move_response.dragged() || move_response.clicked()) if (move_response.dragged() || move_response.clicked())
@ -297,12 +312,13 @@ impl Area {
move_response move_response
}; };
state.pos = ctx.round_pos_to_pixels(state.pos); state.set_left_top_pos(ctx.round_pos_to_pixels(state.left_top_pos()));
if constrain { if constrain {
state.pos = ctx state.set_left_top_pos(
.constrain_window_rect_to_area(state.rect(), drag_bounds) ctx.constrain_window_rect_to_area(state.rect(), drag_bounds)
.min; .left_top(),
);
} }
Prepared { Prepared {
@ -374,14 +390,16 @@ impl Prepared {
}; };
let max_rect = Rect::from_min_max( let max_rect = Rect::from_min_max(
self.state.pos, self.state.left_top_pos(),
bounds.max.at_least(self.state.pos + Vec2::splat(32.0)), bounds
.max
.at_least(self.state.left_top_pos() + Vec2::splat(32.0)),
); );
let shadow_radius = ctx.style().visuals.window_shadow.extrusion; // hacky let shadow_radius = ctx.style().visuals.window_shadow.extrusion; // hacky
let clip_rect_margin = ctx.style().visuals.clip_rect_margin.max(shadow_radius); let clip_rect_margin = ctx.style().visuals.clip_rect_margin.max(shadow_radius);
let clip_rect = Rect::from_min_max(self.state.pos, bounds.max) let clip_rect = Rect::from_min_max(self.state.left_top_pos(), bounds.max)
.expand(clip_rect_margin) .expand(clip_rect_margin)
.intersect(bounds); .intersect(bounds);

View file

@ -438,9 +438,12 @@ impl<'open> Window<'open> {
content_inner content_inner
}; };
area.state_mut().pos = ctx {
let pos = ctx
.constrain_window_rect_to_area(area.state().rect(), area.drag_bounds()) .constrain_window_rect_to_area(area.state().rect(), area.drag_bounds())
.min; .left_top();
area.state_mut().set_left_top_pos(pos);
}
let full_response = area.end(ctx, area_content_ui); let full_response = area.end(ctx, area_content_ui);
@ -550,7 +553,7 @@ fn interact(
let new_rect = ctx.constrain_window_rect_to_area(new_rect, area.drag_bounds()); let new_rect = ctx.constrain_window_rect_to_area(new_rect, area.drag_bounds());
// TODO(emilk): add this to a Window state instead as a command "move here next frame" // TODO(emilk): add this to a Window state instead as a command "move here next frame"
area.state_mut().pos = new_rect.min; area.state_mut().set_left_top_pos(new_rect.left_top());
if window_interaction.is_resize() { if window_interaction.is_resize() {
if let Some(mut state) = resize::State::load(ctx, resize_id) { if let Some(mut state) = resize::State::load(ctx, resize_id) {

View file

@ -105,7 +105,8 @@ impl ContextImpl {
self.memory.areas.set_state( self.memory.areas.set_state(
LayerId::background(), LayerId::background(),
containers::area::State { containers::area::State {
pos: screen_rect.min, pivot_pos: screen_rect.left_top(),
pivot: Align2::LEFT_TOP,
size: screen_rect.size(), size: screen_rect.size(),
interactable: true, interactable: true,
}, },