Skip to content

Commit ddf9d26

Browse files
authored
Fix in Scene: make scene_rect full size on reset (#5801)
<!-- Please read the "Making a PR" section of [`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/master/CONTRIBUTING.md) before opening a Pull Request! * Keep your PR:s small and focused. * The PR title is what ends up in the changelog, so make it descriptive! * If applicable, add a screenshot or gif. * If it is a non-trivial addition, consider adding a demo for it to `egui_demo_lib`, or a new example. * Do NOT open PR:s from your `master` branch, as that makes it hard for maintainers to test and add commits to your PR. * Remember to run `cargo fmt` and `cargo clippy`. * Open the PR as a draft until you have self-reviewed it and run `./scripts/check.sh`. * When you have addressed a PR comment, mark it as resolved. Please be patient! I will review your PR, but my time is limited! --> * [x] I have followed the instructions in the PR template # Overview This is a small change that supports draggable elements inside a `Scene`. When a Scene is initialized with a `Rect::Zero`, following the [example in the demo](https://github.com/emilk/egui/blob/master/crates/egui_demo_lib/src/demo/scene.rs#L15), it will [automatically be reset to the `inner_rect` of the UI](https://github.com/emilk/egui/blob/master/crates/egui/src/containers/scene.rs#L120-L123). This centers the scene on the inner-rect contents, however the resulting `scene_rect` doesn't fill the entire `outer_rect`. This probably isn't an issue for most users of `Scene`. However, I want to support draggable elements on a `Scene`, and to do that I need to map the pointer-position in the window to the scene_rect position. As is, the example of draggable elements on Scene works after the user has modified the scene rect in some way (zoom or pan), when `scene_rect` is set to `to_global.inverse() * outer_rect` ([here](https://github.com/emilk/egui/blob/master/crates/egui/src/containers/scene.rs#L114-L118)). Before a user modifies the scene rect, the pointer-position cannot be reliably mapped to the scene_rect, since the scene_rect doesn't span the entire window. This PR just forces that translation to always run after the scene_rect is reset to `inner_rect`. The practical result is that the scene_rect will now always span the full outer_rect. # Example Here's a small app that demonstrates the functionality I'm trying to support. I'm new to Egui so there may be better patterns for what I'm trying to do, but if you run this against `main` and this branch you'll notice the difference. ```rs use eframe::egui::*; /// Map coordinates from the src rect to the target rect fn map_to_rect(position: Pos2, src_rect: Rect, dest_rect: Rect) -> Pos2 { let x = (position.x - src_rect.min.x) / (src_rect.max.x - src_rect.min.x) * (dest_rect.max.x - dest_rect.min.x) + dest_rect.min.x; let y = (position.y - src_rect.min.y) / (src_rect.max.y - src_rect.min.y) * (dest_rect.max.y - dest_rect.min.y) + dest_rect.min.y; Pos2::new(x, y) } pub fn draggable_scene_element( ui: &mut Ui, id: Id, position: &mut Rect, scene_rect: Rect, container_rect: Rect, ) -> Response { let is_being_dragged = ui.ctx().is_being_dragged(id); if is_being_dragged { let r = ui.put(*position, |ui: &mut Ui| ui.label("Draggable")); if let Some(pointer_pos) = ui.ctx().pointer_interact_pos() { let pointer_pos = map_to_rect(pointer_pos, container_rect, scene_rect); let delta = pointer_pos.to_vec2() - position.center().to_vec2(); *position = position.translate(delta); }; r } else { let r = ui.put(*position, |ui: &mut Ui| ui.label("Draggable")); ui .interact(position.clone(), id, Sense::drag()) .on_hover_cursor(CursorIcon::Grab); r } } struct MyApp { scene_rect: Rect, position: Rect, } impl MyApp { fn new() -> Self { Self { scene_rect: Rect::ZERO, position: Rect::from_min_size(Pos2::new(-50., -50.), Vec2::new(100., 100.)), } } } impl eframe::App for MyApp { fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) { CentralPanel::default().show(ctx, |ui| { let scene_rect = self.scene_rect.clone(); let container_rect = ui.min_rect(); Scene::default().show(ui, &mut self.scene_rect, |ui| { ui.put( Rect::from_min_size(Pos2::new(100., 200.), Vec2::new(100., 100.)), |ui: &mut Ui| ui.label("static element"), ); ui.put(self.position, |ui: &mut Ui| { draggable_scene_element( ui, Id::from("demo"), &mut self.position, scene_rect, container_rect, ) }); }); }); } } ``` # Summary I need a way to map pointer coordinates to scene coordinates, in order to support draggable elements in a scene. This patch makes that easier by ensuring the scene_rect will always be the full size of the outer_rect. If you have a better way to accomplish what I'm after, I'm happy to close this. Thanks!
1 parent 2024295 commit ddf9d26

File tree

2 files changed

+5
-3
lines changed

2 files changed

+5
-3
lines changed

crates/egui/src/containers/scene.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ impl Scene {
119119

120120
if !scene_rect_was_good {
121121
// Auto-reset if the transformation goes bad somehow (or started bad).
122-
*scene_rect = inner_rect;
122+
// Recalculates transform based on inner_rect, resulting in a rect that's the full size of outer_rect but centered on inner_rect.
123+
let to_global = fit_to_rect_in_scene(outer_rect, inner_rect, self.zoom_range);
124+
*scene_rect = to_global.inverse() * outer_rect;
123125
}
124126

125127
ret
Loading

0 commit comments

Comments
 (0)