summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/window_sizer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/ui/window_sizer.cc')
-rw-r--r--chrome/browser/ui/window_sizer.cc336
1 files changed, 336 insertions, 0 deletions
diff --git a/chrome/browser/ui/window_sizer.cc b/chrome/browser/ui/window_sizer.cc
new file mode 100644
index 0000000..292f5fc
--- /dev/null
+++ b/chrome/browser/ui/window_sizer.cc
@@ -0,0 +1,336 @@
+// Copyright (c) 2010 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 "chrome/browser/ui/window_sizer.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/common/pref_names.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// An implementation of WindowSizer::StateProvider that gets the last active
+// and persistent state from the browser window and the user's profile.
+class DefaultStateProvider : public WindowSizer::StateProvider {
+ public:
+ explicit DefaultStateProvider(const std::string& app_name, Browser* browser)
+ : app_name_(app_name),
+ browser_(browser) {
+ }
+
+ // Overridden from WindowSizer::StateProvider:
+ virtual bool GetPersistentState(gfx::Rect* bounds,
+ bool* maximized,
+ gfx::Rect* work_area) const {
+ DCHECK(bounds && maximized);
+
+ std::string key(prefs::kBrowserWindowPlacement);
+ if (!app_name_.empty()) {
+ key.append("_");
+ key.append(app_name_);
+ }
+
+ if (!g_browser_process->local_state())
+ return false;
+
+ const DictionaryValue* wp_pref =
+ g_browser_process->local_state()->GetDictionary(key.c_str());
+ int top = 0, left = 0, bottom = 0, right = 0;
+ bool has_prefs =
+ wp_pref &&
+ wp_pref->GetInteger("top", &top) &&
+ wp_pref->GetInteger("left", &left) &&
+ wp_pref->GetInteger("bottom", &bottom) &&
+ wp_pref->GetInteger("right", &right) &&
+ wp_pref->GetBoolean("maximized", maximized);
+ bounds->SetRect(left, top, std::max(0, right - left),
+ std::max(0, bottom - top));
+
+ int work_area_top = 0;
+ int work_area_left = 0;
+ int work_area_bottom = 0;
+ int work_area_right = 0;
+ if (wp_pref) {
+ wp_pref->GetInteger("work_area_top", &work_area_top);
+ wp_pref->GetInteger("work_area_left", &work_area_left);
+ wp_pref->GetInteger("work_area_bottom", &work_area_bottom);
+ wp_pref->GetInteger("work_area_right", &work_area_right);
+ }
+ work_area->SetRect(work_area_left, work_area_top,
+ std::max(0, work_area_right - work_area_left),
+ std::max(0, work_area_bottom - work_area_top));
+
+ return has_prefs;
+ }
+
+ virtual bool GetLastActiveWindowState(gfx::Rect* bounds) const {
+ // Applications are always restored with the same position.
+ if (!app_name_.empty())
+ return false;
+
+ // If a reference browser is set, use its window. Otherwise find last
+ // active.
+ BrowserWindow* window = NULL;
+ if (browser_) {
+ window = browser_->window();
+ DCHECK(window);
+ } else {
+ BrowserList::const_reverse_iterator it = BrowserList::begin_last_active();
+ BrowserList::const_reverse_iterator end = BrowserList::end_last_active();
+ for (; (it != end); ++it) {
+ Browser* last_active = *it;
+ if (last_active && last_active->type() == Browser::TYPE_NORMAL) {
+ window = last_active->window();
+ DCHECK(window);
+ break;
+ }
+ }
+ }
+
+ if (window) {
+ *bounds = window->GetRestoredBounds();
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ std::string app_name_;
+
+ // If set, is used as the reference browser for GetLastActiveWindowState.
+ Browser* browser_;
+ DISALLOW_COPY_AND_ASSIGN(DefaultStateProvider);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// MonitorInfoProvider, public:
+
+WindowSizer::MonitorInfoProvider::MonitorInfoProvider() {}
+
+WindowSizer::MonitorInfoProvider::~MonitorInfoProvider() {}
+
+///////////////////////////////////////////////////////////////////////////////
+// WindowSizer, public:
+
+WindowSizer::WindowSizer(
+ StateProvider* state_provider,
+ MonitorInfoProvider* monitor_info_provider) {
+ Init(state_provider, monitor_info_provider);
+}
+
+WindowSizer::~WindowSizer() {
+ if (state_provider_)
+ delete state_provider_;
+ if (monitor_info_provider_)
+ delete monitor_info_provider_;
+}
+
+// static
+void WindowSizer::GetBrowserWindowBounds(const std::string& app_name,
+ const gfx::Rect& specified_bounds,
+ Browser* browser,
+ gfx::Rect* window_bounds,
+ bool* maximized) {
+ const WindowSizer sizer(new DefaultStateProvider(app_name, browser),
+ CreateDefaultMonitorInfoProvider());
+ sizer.DetermineWindowBounds(specified_bounds, window_bounds, maximized);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// WindowSizer, private:
+
+WindowSizer::WindowSizer(const std::string& app_name) {
+ Init(new DefaultStateProvider(app_name, NULL),
+ CreateDefaultMonitorInfoProvider());
+}
+
+void WindowSizer::Init(StateProvider* state_provider,
+ MonitorInfoProvider* monitor_info_provider) {
+ state_provider_ = state_provider;
+ monitor_info_provider_ = monitor_info_provider;
+}
+
+void WindowSizer::DetermineWindowBounds(const gfx::Rect& specified_bounds,
+ gfx::Rect* bounds,
+ bool* maximized) const {
+ *bounds = specified_bounds;
+ if (bounds->IsEmpty()) {
+ // See if there's saved placement information.
+ if (!GetLastWindowBounds(bounds)) {
+ if (!GetSavedWindowBounds(bounds, maximized)) {
+ // No saved placement, figure out some sensible default size based on
+ // the user's screen size.
+ GetDefaultWindowBounds(bounds);
+ }
+ }
+ }
+}
+
+bool WindowSizer::GetLastWindowBounds(gfx::Rect* bounds) const {
+ DCHECK(bounds);
+ if (!state_provider_ || !state_provider_->GetLastActiveWindowState(bounds))
+ return false;
+ gfx::Rect last_window_bounds = *bounds;
+ bounds->Offset(kWindowTilePixels, kWindowTilePixels);
+ AdjustBoundsToBeVisibleOnMonitorContaining(last_window_bounds,
+ gfx::Rect(),
+ bounds);
+ return true;
+}
+
+bool WindowSizer::GetSavedWindowBounds(gfx::Rect* bounds,
+ bool* maximized) const {
+ DCHECK(bounds && maximized);
+ gfx::Rect saved_work_area;
+ if (!state_provider_ ||
+ !state_provider_->GetPersistentState(bounds, maximized, &saved_work_area))
+ return false;
+ AdjustBoundsToBeVisibleOnMonitorContaining(*bounds, saved_work_area, bounds);
+ return true;
+}
+
+void WindowSizer::GetDefaultWindowBounds(gfx::Rect* default_bounds) const {
+ DCHECK(default_bounds);
+ DCHECK(monitor_info_provider_);
+
+ gfx::Rect work_area = monitor_info_provider_->GetPrimaryMonitorWorkArea();
+
+ // The default size is either some reasonably wide width, or if the work
+ // area is narrower, then the work area width less some aesthetic padding.
+ int default_width = std::min(work_area.width() - 2 * kWindowTilePixels, 1050);
+ int default_height = work_area.height() - 2 * kWindowTilePixels;
+
+ // For wider aspect ratio displays at higher resolutions, we might size the
+ // window narrower to allow two windows to easily be placed side-by-side.
+ gfx::Rect screen_size = monitor_info_provider_->GetPrimaryMonitorBounds();
+ double width_to_height =
+ static_cast<double>(screen_size.width()) / screen_size.height();
+
+ // The least wide a screen can be to qualify for the halving described above.
+ static const int kMinScreenWidthForWindowHalving = 1600;
+ // We assume 16:9/10 is a fairly standard indicator of a wide aspect ratio
+ // computer display.
+ if (((width_to_height * 10) >= 16) &&
+ work_area.width() > kMinScreenWidthForWindowHalving) {
+ // Halve the work area, subtracting aesthetic padding on either side.
+ // The padding is set so that two windows, side by side have
+ // kWindowTilePixels between screen edge and each other.
+ default_width = static_cast<int>(work_area.width() / 2. -
+ 1.5 * kWindowTilePixels);
+ }
+ default_bounds->SetRect(kWindowTilePixels + work_area.x(),
+ kWindowTilePixels + work_area.y(),
+ default_width, default_height);
+}
+
+bool WindowSizer::PositionIsOffscreen(int position, Edge edge) const {
+ DCHECK(monitor_info_provider_);
+ size_t monitor_count = monitor_info_provider_->GetMonitorCount();
+ for (size_t i = 0; i < monitor_count; ++i) {
+ gfx::Rect work_area = monitor_info_provider_->GetWorkAreaAt(i);
+ switch (edge) {
+ case TOP:
+ if (position >= work_area.y())
+ return false;
+ break;
+ case LEFT:
+ if (position >= work_area.x())
+ return false;
+ break;
+ case BOTTOM:
+ if (position <= work_area.bottom())
+ return false;
+ break;
+ case RIGHT:
+ if (position <= work_area.right())
+ return false;
+ break;
+ }
+ }
+ return true;
+}
+
+namespace {
+ // Minimum height of the visible part of a window.
+ static const int kMinVisibleHeight = 30;
+ // Minimum width of the visible part of a window.
+ static const int kMinVisibleWidth = 30;
+}
+
+void WindowSizer::AdjustBoundsToBeVisibleOnMonitorContaining(
+ const gfx::Rect& other_bounds,
+ const gfx::Rect& saved_work_area,
+ gfx::Rect* bounds) const {
+ DCHECK(bounds);
+ DCHECK(monitor_info_provider_);
+
+ // Find the size of the work area of the monitor that intersects the bounds
+ // of the anchor window.
+ gfx::Rect work_area =
+ monitor_info_provider_->GetMonitorWorkAreaMatching(other_bounds);
+
+ // If height or width are 0, reset to the default size.
+ gfx::Rect default_bounds;
+ GetDefaultWindowBounds(&default_bounds);
+ if (bounds->height() <= 0)
+ bounds->set_height(default_bounds.height());
+ if (bounds->width() <= 0)
+ bounds->set_width(default_bounds.width());
+
+ // Ensure the minimum height and width.
+ bounds->set_height(std::max(kMinVisibleHeight, bounds->height()));
+ bounds->set_width(std::max(kMinVisibleWidth, bounds->width()));
+
+ // Ensure that the title bar is not above the work area.
+ if (bounds->y() < work_area.y())
+ bounds->set_y(work_area.y());
+
+ // Reposition and resize the bounds if the saved_work_area is different from
+ // the current work area and the current work area doesn't completely contain
+ // the bounds.
+ if (!saved_work_area.IsEmpty() &&
+ saved_work_area != work_area &&
+ !work_area.Contains(*bounds)) {
+ bounds->set_width(std::min(bounds->width(), work_area.width()));
+ bounds->set_height(std::min(bounds->height(), work_area.height()));
+ bounds->set_x(
+ std::max(work_area.x(),
+ std::min(bounds->x(), work_area.right() - bounds->width())));
+ bounds->set_y(
+ std::max(work_area.y(),
+ std::min(bounds->y(), work_area.bottom() - bounds->height())));
+ }
+
+#if defined(OS_MACOSX)
+ // Limit the maximum height. On the Mac the sizer is on the
+ // bottom-right of the window, and a window cannot be moved "up"
+ // past the menubar. If the window is too tall you'll never be able
+ // to shrink it again. Windows does not have this limitation
+ // (e.g. can be resized from the top).
+ bounds->set_height(std::min(work_area.height(), bounds->height()));
+
+ // On mac, we want to be aggressive about repositioning windows that are
+ // partially offscreen. If the window is partially offscreen horizontally,
+ // move it to be flush with the left edge of the work area.
+ if (bounds->x() < work_area.x() || bounds->right() > work_area.right())
+ bounds->set_x(work_area.x());
+
+ // If the window is partially offscreen vertically, move it to be flush with
+ // the top of the work area.
+ if (bounds->y() < work_area.y() || bounds->bottom() > work_area.bottom())
+ bounds->set_y(work_area.y());
+#else
+ // On non-Mac platforms, we are less aggressive about repositioning. Simply
+ // ensure that at least kMinVisibleWidth * kMinVisibleHeight is visible.
+ const int min_y = work_area.y() + kMinVisibleHeight - bounds->height();
+ const int min_x = work_area.x() + kMinVisibleWidth - bounds->width();
+ const int max_y = work_area.bottom() - kMinVisibleHeight;
+ const int max_x = work_area.right() - kMinVisibleWidth;
+ bounds->set_y(std::max(min_y, std::min(max_y, bounds->y())));
+ bounds->set_x(std::max(min_x, std::min(max_x, bounds->x())));
+#endif // defined(OS_MACOSX)
+}