diff options
author | oshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-04 03:20:01 +0000 |
---|---|---|
committer | oshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-04 03:20:01 +0000 |
commit | e9a71317209f27bb07ccd72d34e6c40e8469556f (patch) | |
tree | accfb9eda5029197dbd71d3a44f22e46b9015f21 /ash | |
parent | 919b2f83216c7a9f07d2e3569ad74ab8c1a06675 (diff) | |
download | chromium_src-e9a71317209f27bb07ccd72d34e6c40e8469556f.zip chromium_src-e9a71317209f27bb07ccd72d34e6c40e8469556f.tar.gz chromium_src-e9a71317209f27bb07ccd72d34e6c40e8469556f.tar.bz2 |
Move WindowPositioner to ash/wm
WindowPositioner doesn't depend on chrome at all.
window_positioner_unittest remains c/b/ui/ash as it depends on browser. I'll add separate tests in ash for this.
BUG=272460
TBR=sky@chromium.org
Review URL: https://codereview.chromium.org/25852004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@226941 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r-- | ash/ash.gyp | 2 | ||||
-rw-r--r-- | ash/shell.cc | 2 | ||||
-rw-r--r-- | ash/shell.h | 7 | ||||
-rw-r--r-- | ash/wm/window_positioner.cc | 188 | ||||
-rw-r--r-- | ash/wm/window_positioner.h | 75 |
5 files changed, 273 insertions, 1 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp index 7793fc0..aee601b 100644 --- a/ash/ash.gyp +++ b/ash/ash.gyp @@ -515,6 +515,8 @@ 'wm/window_cycle_controller.h', 'wm/window_cycle_list.cc', 'wm/window_cycle_list.h', + 'wm/window_positioner.cc', + 'wm/window_positioner.h', 'wm/window_state.cc', 'wm/window_state.h', 'wm/window_state_observer.h', diff --git a/ash/shell.cc b/ash/shell.cc index ccdbae2..8bbad04 100644 --- a/ash/shell.cc +++ b/ash/shell.cc @@ -70,6 +70,7 @@ #include "ash/wm/video_detector.h" #include "ash/wm/window_animations.h" #include "ash/wm/window_cycle_controller.h" +#include "ash/wm/window_positioner.h" #include "ash/wm/window_properties.h" #include "ash/wm/window_util.h" #include "ash/wm/workspace_controller.h" @@ -162,6 +163,7 @@ Shell::Shell(ShellDelegate* delegate) target_root_window_(NULL), scoped_target_root_window_(NULL), delegate_(delegate), + window_positioner_(new WindowPositioner), activation_client_(NULL), #if defined(OS_CHROMEOS) && defined(USE_X11) output_configurator_(new chromeos::OutputConfigurator()), diff --git a/ash/shell.h b/ash/shell.h index ed007b9..74c6311 100644 --- a/ash/shell.h +++ b/ash/shell.h @@ -97,6 +97,7 @@ class UserWallpaperDelegate; class VideoDetector; class WebNotificationTray; class WindowCycleController; +class WindowPositioner; class WindowSelectorController; namespace internal { @@ -120,7 +121,6 @@ class OverlayEventFilter; class ResizeShadowController; class ResolutionNotificationController; class RootWindowController; -class RootWindowLayoutManager; class ScopedTargetRootWindow; class ScreenPositionController; class SlowAnimationEventFilter; @@ -473,6 +473,10 @@ class ASH_EXPORT Shell return launcher_model_.get(); } + WindowPositioner* window_positioner() { + return window_positioner_.get(); + } + // Returns the launcher delegate, creating if necesary. LauncherDelegate* GetLauncherDelegate(); @@ -551,6 +555,7 @@ class ASH_EXPORT Shell app_list_shelf_item_delegate_; scoped_ptr<LauncherModel> launcher_model_; + scoped_ptr<ash::WindowPositioner> window_positioner_; scoped_ptr<internal::AppListController> app_list_controller_; diff --git a/ash/wm/window_positioner.cc b/ash/wm/window_positioner.cc new file mode 100644 index 0000000..1a7af44 --- /dev/null +++ b/ash/wm/window_positioner.cc @@ -0,0 +1,188 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/wm/window_positioner.h" + +#include "ash/shell.h" +#include "ash/wm/mru_window_tracker.h" +#include "ash/wm/window_resizer.h" +#include "ash/wm/window_state.h" +#include "ash/wm/window_util.h" +#include "ui/aura/window.h" +#include "ui/aura/window_delegate.h" +#include "ui/compositor/layer.h" +#include "ui/gfx/screen.h" + +namespace ash { + +// statics + +const int WindowPositioner::kMinimumWindowOffset = 32; + +WindowPositioner::WindowPositioner() + : pop_position_offset_increment_x(0), + pop_position_offset_increment_y(0), + popup_position_offset_from_screen_corner_x(0), + popup_position_offset_from_screen_corner_y(0), + last_popup_position_x_(0), + last_popup_position_y_(0) { +} + +WindowPositioner::~WindowPositioner() { +} + +gfx::Rect WindowPositioner::GetPopupPosition(const gfx::Rect& old_pos) { + int grid = kMinimumWindowOffset; + popup_position_offset_from_screen_corner_x = grid; + popup_position_offset_from_screen_corner_y = grid; + if (!pop_position_offset_increment_x) { + // When the popup position increment is , the last popup position + // was not yet initialized. + last_popup_position_x_ = popup_position_offset_from_screen_corner_x; + last_popup_position_y_ = popup_position_offset_from_screen_corner_y; + } + pop_position_offset_increment_x = grid; + pop_position_offset_increment_y = grid; + // We handle the Multi monitor support by retrieving the active window's + // work area. + aura::Window* window = ash::wm::GetActiveWindow(); + const gfx::Rect work_area = window && window->IsVisible() ? + Shell::GetScreen()->GetDisplayNearestWindow(window).work_area() : + Shell::GetScreen()->GetPrimaryDisplay().work_area(); + // Only try to reposition the popup when it is not spanning the entire + // screen. + if ((old_pos.width() + popup_position_offset_from_screen_corner_x >= + work_area.width()) || + (old_pos.height() + popup_position_offset_from_screen_corner_y >= + work_area.height())) + return AlignPopupPosition(old_pos, work_area, grid); + const gfx::Rect result = SmartPopupPosition(old_pos, work_area, grid); + if (!result.IsEmpty()) + return AlignPopupPosition(result, work_area, grid); + return NormalPopupPosition(old_pos, work_area); +} + +gfx::Rect WindowPositioner::NormalPopupPosition( + const gfx::Rect& old_pos, + const gfx::Rect& work_area) { + int w = old_pos.width(); + int h = old_pos.height(); + // Note: The 'last_popup_position' is checked and kept relative to the + // screen size. The offsetting will be done in the last step when the + // target rectangle gets returned. + bool reset = false; + if (last_popup_position_y_ + h > work_area.height() || + last_popup_position_x_ + w > work_area.width()) { + // Popup does not fit on screen. Reset to next diagonal row. + last_popup_position_x_ -= last_popup_position_y_ - + popup_position_offset_from_screen_corner_x - + pop_position_offset_increment_x; + last_popup_position_y_ = popup_position_offset_from_screen_corner_y; + reset = true; + } + if (last_popup_position_x_ + w > work_area.width()) { + // Start again over. + last_popup_position_x_ = popup_position_offset_from_screen_corner_x; + last_popup_position_y_ = popup_position_offset_from_screen_corner_y; + reset = true; + } + int x = last_popup_position_x_; + int y = last_popup_position_y_; + if (!reset) { + last_popup_position_x_ += pop_position_offset_increment_x; + last_popup_position_y_ += pop_position_offset_increment_y; + } + return gfx::Rect(x + work_area.x(), y + work_area.y(), w, h); +} + +gfx::Rect WindowPositioner::SmartPopupPosition( + const gfx::Rect& old_pos, + const gfx::Rect& work_area, + int grid) { + const std::vector<aura::Window*> windows = + ash::MruWindowTracker::BuildWindowList(false); + + std::vector<const gfx::Rect*> regions; + // Process the window list and check if we can bail immediately. + for (size_t i = 0; i < windows.size(); i++) { + // We only include opaque and visible windows. + if (windows[i] && windows[i]->IsVisible() && windows[i]->layer() && + (!windows[i]->transparent() || + windows[i]->layer()->GetTargetOpacity() == 1.0)) { + ash::wm::WindowState* window_state = ash::wm::GetWindowState(windows[i]); + // When any window is maximized we cannot find any free space. + if (window_state->IsMaximizedOrFullscreen()) + return gfx::Rect(0, 0, 0, 0); + if (window_state->IsNormalShowState()) + regions.push_back(&windows[i]->bounds()); + } + } + + if (regions.empty()) + return gfx::Rect(0, 0, 0, 0); + + int w = old_pos.width(); + int h = old_pos.height(); + int x_end = work_area.width() / 2; + int x, x_increment; + // We parse for a proper location on the screen. We do this in two runs: + // The first run will start from the left, parsing down, skipping any + // overlapping windows it will encounter until the popup's height can not + // be served anymore. Then the next grid position to the right will be + // taken, and the same cycle starts again. This will be repeated until we + // hit the middle of the screen (or we find a suitable location). + // In the second run we parse beginning from the right corner downwards and + // then to the left. + // When no location was found, an empty rectangle will be returned. + for (int run = 0; run < 2; run++) { + if (run == 0) { // First run: Start left, parse right till mid screen. + x = 0; + x_increment = pop_position_offset_increment_x; + } else { // Second run: Start right, parse left till mid screen. + x = work_area.width() - w; + x_increment = -pop_position_offset_increment_x; + } + // Note: The passing (x,y,w,h) window is always relative to the work area's + // origin. + for (; x_increment > 0 ? (x < x_end) : (x > x_end); x += x_increment) { + int y = 0; + while (y + h <= work_area.height()) { + size_t i; + for (i = 0; i < regions.size(); i++) { + if (regions[i]->Intersects(gfx::Rect(x + work_area.x(), + y + work_area.y(), w, h))) { + y = regions[i]->bottom() - work_area.y(); + break; + } + } + if (i >= regions.size()) + return gfx::Rect(x + work_area.x(), y + work_area.y(), w, h); + } + } + } + return gfx::Rect(0, 0, 0, 0); +} + +gfx::Rect WindowPositioner::AlignPopupPosition( + const gfx::Rect& pos, + const gfx::Rect& work_area, + int grid) { + if (grid <= 1) + return pos; + + int x = pos.x() - (pos.x() - work_area.x()) % grid; + int y = pos.y() - (pos.y() - work_area.y()) % grid; + int w = pos.width(); + int h = pos.height(); + + // If the alignment was pushing the window out of the screen, we ignore the + // alignment for that call. + if (abs(pos.right() - work_area.right()) < grid) + x = work_area.right() - w; + if (abs(pos.bottom() - work_area.bottom()) < grid) + y = work_area.bottom() - h; + return gfx::Rect(x, y, w, h); +} + +} // namespace ash diff --git a/ash/wm/window_positioner.h b/ash/wm/window_positioner.h new file mode 100644 index 0000000..8a95b2e --- /dev/null +++ b/ash/wm/window_positioner.h @@ -0,0 +1,75 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_WM_WINDOW_POSITIONER_H_ +#define ASH_WM_WINDOW_POSITIONER_H_ + +#include "ash/ash_export.h" +#include "base/basictypes.h" +#include "ui/gfx/rect.h" + +namespace aura { +class Window; +} + +namespace ash { + +namespace test { +class WindowPositionerTest; +} + +// WindowPositioner is used by the browser to move new popups automatically to +// a usable position on the closest work area (of the active window). +class ASH_EXPORT WindowPositioner { + public: + WindowPositioner(); + ~WindowPositioner(); + + // Find a suitable screen position for a popup window and return it. The + // passed input position is only used to retrieve the width and height. + // The position is determined on the left / right / top / bottom first. If + // no smart space is found, the position will follow the standard what other + // operating systems do (default cascading style). + gfx::Rect GetPopupPosition(const gfx::Rect& old_pos); + + protected: + friend class test::WindowPositionerTest; + + // Find a smart way to position the popup window. If there is no space this + // function will return an empty rectangle. + gfx::Rect SmartPopupPosition(const gfx::Rect& old_pos, + const gfx::Rect& work_area, + int grid); + + // Find the next available cascading popup position (on the given screen). + gfx::Rect NormalPopupPosition(const gfx::Rect& old_pos, + const gfx::Rect& work_area); + + // Align the location to the grid / snap to the right / bottom corner. + gfx::Rect AlignPopupPosition(const gfx::Rect &pos, + const gfx::Rect &work_area, + int grid); + + // Constant exposed for unittest. + static const int kMinimumWindowOffset; + + // The offset in X and Y for the next popup which opens. + int pop_position_offset_increment_x; + int pop_position_offset_increment_y; + + // The position on the screen for the first popup which gets shown if no + // empty space can be found. + int popup_position_offset_from_screen_corner_x; + int popup_position_offset_from_screen_corner_y; + + // The last used position. + int last_popup_position_x_; + int last_popup_position_y_; + + DISALLOW_COPY_AND_ASSIGN(WindowPositioner); +}; + +} // namespace ash + +#endif // ASH_WM_WINDOW_POSITIONER_H_ |