diff --git a/CHANGELOG.md b/CHANGELOG.md index da5bc961..48e29634 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Add ability to override text color with `visuals.override_text_color` * Refactored the interface for `egui::app::App` * Demo App: Add slider to scale all of Egui +* Windows are now constrained to the screen * Fix a bug where some regions would slowly grow for non-integral scales (`pixels_per_point`). ## 0.2.0 - 2020-10-10 diff --git a/egui/src/containers/area.rs b/egui/src/containers/area.rs index b38936ee..5fbcf7f7 100644 --- a/egui/src/containers/area.rs +++ b/egui/src/containers/area.rs @@ -175,8 +175,8 @@ impl Prepared { state.size = content_ui.min_rect().size(); - let rect = Rect::from_min_size(state.pos, state.size); - let clip_rect = Rect::everything(); // TODO: get from context + let area_rect = Rect::from_min_size(state.pos, state.size); + let clip_rect = ctx.rect(); let interact_id = if movable { Some(layer_id.id.with("move")) @@ -186,7 +186,7 @@ impl Prepared { let move_response = ctx.interact( layer_id, clip_rect, - rect, + area_rect, interact_id, Sense::click_and_drag(), ); @@ -210,14 +210,7 @@ impl Prepared { } } - // Constrain to screen: - let margin = 32.0; - state.pos = state.pos.max(pos2(margin - state.size.x, 0.0)); - state.pos = state.pos.min(pos2( - ctx.input().screen_size.x - margin, - ctx.input().screen_size.y - margin, - )); - state.pos = ctx.round_pos_to_pixels(state.pos); + state.pos = ctx.constrain_window_rect(area_rect).min; if move_response.active || mouse_pressed_on_area(ctx, layer_id) diff --git a/egui/src/containers/window.rs b/egui/src/containers/window.rs index 412335fb..ecb90448 100644 --- a/egui/src/containers/window.rs +++ b/egui/src/containers/window.rs @@ -384,11 +384,11 @@ fn interact( area_state: &mut area::State, resize_id: Id, ) -> Option { - let new_rect = resize_window(ctx, &window_interaction)?; - + let new_rect = move_and_resize_window(ctx, &window_interaction)?; let new_rect = ctx.round_rect_to_pixels(new_rect); - // TODO: add this to a Window state instead as a command "move here next frame" + let new_rect = ctx.constrain_window_rect(new_rect); + // TODO: add this to a Window state instead as a command "move here next frame" area_state.pos = new_rect.min; if window_interaction.is_resize() { @@ -401,7 +401,7 @@ fn interact( Some(window_interaction) } -fn resize_window(ctx: &Context, window_interaction: &WindowInteraction) -> Option { +fn move_and_resize_window(ctx: &Context, window_interaction: &WindowInteraction) -> Option { window_interaction.set_cursor(ctx); let mouse_pos = ctx.input().mouse.pos?; let mut rect = window_interaction.start_rect; // prevent drift diff --git a/egui/src/context.rs b/egui/src/context.rs index 32bafec6..0554b161 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -165,6 +165,29 @@ impl Context { // --------------------------------------------------------------------- + /// Constraint the position of a window/area + /// so it fits within the screen. + pub(crate) fn constrain_window_rect(&self, window: Rect) -> Rect { + let screen = self.rect(); + + let mut pos = window.min; + + // Constrain to screen, unless window is too large to fit: + let margin_x = (window.width() - screen.width()).at_least(0.0); + let margin_y = (window.height() - screen.height()).at_least(0.0); + + pos.x = pos.x.at_least(screen.left() - margin_x); + pos.x = pos.x.at_most(screen.right() + margin_x - window.width()); + pos.y = pos.y.at_least(screen.top() - margin_y); + pos.y = pos.y.at_most(screen.bottom() + margin_y - window.height()); + + pos = self.round_pos_to_pixels(pos); + + Rect::from_min_size(pos, window.size()) + } + + // --------------------------------------------------------------------- + /// Call at the start of every frame. /// Returns a master fullscreen UI, covering the entire screen. pub fn begin_frame(self: &mut Arc, new_input: RawInput) -> Ui {