Skip to content

Commit

Permalink
Fix Window::pivot causing windows to move around (#2694)
Browse files Browse the repository at this point in the history
* Fix Window::pivot causing windows to move around

* Add line to changelog
  • Loading branch information
emilk authored Feb 8, 2023
1 parent a8d5a82 commit e8b9e70
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)).
* Fixed rendering of `` (ellipsis).
* 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
Expand Down
62 changes: 40 additions & 22 deletions crates/egui/src/containers/area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ use crate::*;
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub(crate) struct State {
/// Last known pos
pub pos: Pos2,
/// Last known pos of the pivot
pub pivot_pos: Pos2,

pub pivot: Align2,

/// Last know size. Used for catching clicks.
pub size: Vec2,
Expand All @@ -21,8 +23,22 @@ pub(crate) struct 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 {
Rect::from_min_size(self.pos, self.size)
Rect::from_min_size(self.left_top_pos(), self.size)
}
}

Expand Down Expand Up @@ -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
}
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,
interactable,
});
state.pos = new_pos.unwrap_or(state.pos);
state.pivot_pos = new_pos.unwrap_or(state.pivot_pos);
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 {
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
Expand All @@ -278,12 +292,13 @@ impl Area {
// Important check - don't try to move e.g. a combobox popup!
if movable {
if move_response.dragged() {
state.pos += ctx.input(|i| i.pointer.delta());
state.pivot_pos += ctx.input(|i| i.pointer.delta());
}

state.pos = ctx
.constrain_window_rect_to_area(state.rect(), drag_bounds)
.min;
state.set_left_top_pos(
ctx.constrain_window_rect_to_area(state.rect(), drag_bounds)
.min,
);
}

if (move_response.dragged() || move_response.clicked())
Expand All @@ -297,12 +312,13 @@ impl Area {
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 {
state.pos = ctx
.constrain_window_rect_to_area(state.rect(), drag_bounds)
.min;
state.set_left_top_pos(
ctx.constrain_window_rect_to_area(state.rect(), drag_bounds)
.left_top(),
);
}

Prepared {
Expand Down Expand Up @@ -374,14 +390,16 @@ impl Prepared {
};

let max_rect = Rect::from_min_max(
self.state.pos,
bounds.max.at_least(self.state.pos + Vec2::splat(32.0)),
self.state.left_top_pos(),
bounds
.max
.at_least(self.state.left_top_pos() + Vec2::splat(32.0)),
);

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 = 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)
.intersect(bounds);

Expand Down
11 changes: 7 additions & 4 deletions crates/egui/src/containers/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,9 +438,12 @@ impl<'open> Window<'open> {
content_inner
};

area.state_mut().pos = ctx
.constrain_window_rect_to_area(area.state().rect(), area.drag_bounds())
.min;
{
let pos = ctx
.constrain_window_rect_to_area(area.state().rect(), area.drag_bounds())
.left_top();
area.state_mut().set_left_top_pos(pos);
}

let full_response = area.end(ctx, area_content_ui);

Expand Down Expand Up @@ -550,7 +553,7 @@ fn interact(
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"
area.state_mut().pos = new_rect.min;
area.state_mut().set_left_top_pos(new_rect.left_top());

if window_interaction.is_resize() {
if let Some(mut state) = resize::State::load(ctx, resize_id) {
Expand Down
3 changes: 2 additions & 1 deletion crates/egui/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ impl ContextImpl {
self.memory.areas.set_state(
LayerId::background(),
containers::area::State {
pos: screen_rect.min,
pivot_pos: screen_rect.left_top(),
pivot: Align2::LEFT_TOP,
size: screen_rect.size(),
interactable: true,
},
Expand Down

0 comments on commit e8b9e70

Please sign in to comment.