Commit 51116b9a authored by kylixrd's avatar kylixrd Committed by Commit bot

Clamp dialog bounds to be fully visible on the nearest display

If a displayed modal dialog is partially or entirely off-screen, the
user may be unable to interact with it or even be aware of its
presence. This change ensures that the dialog is moved to the
nearest display so that it is fully visible to the user.

BUG=668734

Review-Url: https://codereview.chromium.org/2549543002
Cr-Commit-Position: refs/heads/master@{#439127}
parent 6e3ac8ac
...@@ -25,6 +25,7 @@ static_library("constrained_window") { ...@@ -25,6 +25,7 @@ static_library("constrained_window") {
"//components/guest_view/browser", "//components/guest_view/browser",
"//components/web_modal", "//components/web_modal",
"//content/public/browser", "//content/public/browser",
"//ui/display",
"//ui/views", "//ui/views",
] ]
public_deps = [ public_deps = [
......
...@@ -4,6 +4,7 @@ include_rules = [ ...@@ -4,6 +4,7 @@ include_rules = [
"+content/public/browser", "+content/public/browser",
"+ui/aura", "+ui/aura",
"+ui/base", "+ui/base",
"+ui/display",
"+ui/gfx", "+ui/gfx",
"+ui/views", "+ui/views",
"+ui/wm", "+ui/wm",
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#include "components/web_modal/web_contents_modal_dialog_host.h" #include "components/web_modal/web_contents_modal_dialog_host.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h" #include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h" #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/views/border.h" #include "ui/views/border.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h" #include "ui/views/widget/widget_observer.h"
...@@ -108,8 +110,20 @@ void UpdateModalDialogPosition(views::Widget* widget, ...@@ -108,8 +110,20 @@ void UpdateModalDialogPosition(views::Widget* widget,
position.set_y(position.y() - border->GetInsets().top()); position.set_y(position.y() - border->GetInsets().top());
} }
if (widget->is_top_level()) if (widget->is_top_level()) {
position += host_widget->GetClientAreaBoundsInScreen().OffsetFromOrigin(); position += host_widget->GetClientAreaBoundsInScreen().OffsetFromOrigin();
// If the dialog extends partially off any display, clamp its position to
// be fully visible within that display. If the dialog doesn't intersect
// with any display clamp its position to be fully on the nearest display.
gfx::Rect display_rect = gfx::Rect(position, size);
const display::Display display =
display::Screen::GetScreen()->GetDisplayNearestWindow(
dialog_host->GetHostView());
const gfx::Rect work_area = display.work_area();
if (!work_area.Contains(display_rect))
display_rect.AdjustToFit(work_area);
position = display_rect.origin();
}
widget->SetBounds(gfx::Rect(position, size)); widget->SetBounds(gfx::Rect(position, size));
} }
......
...@@ -5,10 +5,13 @@ ...@@ -5,10 +5,13 @@
#include "components/constrained_window/constrained_window_views.h" #include "components/constrained_window/constrained_window_views.h"
#include <memory> #include <memory>
#include <vector>
#include "base/macros.h" #include "base/macros.h"
#include "components/constrained_window/constrained_window_views_client.h" #include "components/constrained_window/constrained_window_views_client.h"
#include "components/web_modal/test_web_contents_modal_dialog_host.h" #include "components/web_modal/test_web_contents_modal_dialog_host.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size.h"
...@@ -228,4 +231,38 @@ TEST_F(ConstrainedWindowViewsTest, NullModalParent) { ...@@ -228,4 +231,38 @@ TEST_F(ConstrainedWindowViewsTest, NullModalParent) {
widget->CloseNow(); widget->CloseNow();
} }
// Make sure dialogs presented off-screen are properly clamped to the nearest
// screen.
TEST_F(ConstrainedWindowViewsTest, ClampDialogToNearestDisplay) {
// Make sure the dialog will fit fully on the display
contents()->set_preferred_size(gfx::Size(200, 100));
// First, make sure the host and dialog are sized and positioned.
UpdateWebContentsModalDialogPosition(dialog(), dialog_host());
const display::Screen* screen = display::Screen::GetScreen();
const display::Display display = screen->GetPrimaryDisplay();
// Within the tests there is only 1 display. Error if that ever changes.
EXPECT_EQ(screen->GetNumDisplays(), 1);
const gfx::Rect extents = display.work_area();
// Move the host completely off the screen.
views::Widget* host_widget =
views::Widget::GetWidgetForNativeView(dialog_host()->GetHostView());
gfx::Rect host_bounds = host_widget->GetWindowBoundsInScreen();
host_bounds.set_origin(gfx::Point(extents.right(), extents.bottom()));
host_widget->SetBounds(host_bounds);
// Make sure the host is fully off the screen.
EXPECT_FALSE(extents.Intersects(host_widget->GetWindowBoundsInScreen()));
// Now reposition the modal dialog into the display.
UpdateWebContentsModalDialogPosition(dialog(), dialog_host());
const gfx::Rect dialog_bounds = dialog()->GetRootView()->GetBoundsInScreen();
// The dialog should now be fully on the display.
EXPECT_TRUE(extents.Contains(dialog_bounds));
}
} // namespace constrained_window } // namespace constrained_window
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment