summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorsky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-20 23:07:41 +0000
committersky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-20 23:07:41 +0000
commit5e49546581a5a4c2c02d718f9d6e3dbebd7bebc7 (patch)
tree2a908845ceef1a7df68ff47eed5f2bd54d12cc9e /chrome/browser
parentdcda464d91fb97423f5b551babffe786b24ee251 (diff)
downloadchromium_src-5e49546581a5a4c2c02d718f9d6e3dbebd7bebc7.zip
chromium_src-5e49546581a5a4c2c02d718f9d6e3dbebd7bebc7.tar.gz
chromium_src-5e49546581a5a4c2c02d718f9d6e3dbebd7bebc7.tar.bz2
Adds various docking positions when dragging around a tab. I've added
the ability to dock to the following locations: Maximize the window on drop. Resize the window to fill up the left, bottom, or right half of the monitor. Tile the newly created window with an existing chrome window such that the two are on top of each other and fill the height of the monitor, or tiled such that they fill the width of the monitor. The graphics suxor. Glen says he'll come up with something better once its landed. BUG=none TEST=4628 Review URL: http://codereview.chromium.org/11325 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@5797 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/browser.cc12
-rw-r--r--chrome/browser/browser.h3
-rw-r--r--chrome/browser/browser.scons1
-rw-r--r--chrome/browser/browser.vcproj8
-rw-r--r--chrome/browser/dock_info.cc481
-rw-r--r--chrome/browser/dock_info.h136
-rw-r--r--chrome/browser/tabs/tab_strip_model.cc6
-rw-r--r--chrome/browser/tabs/tab_strip_model.h10
-rw-r--r--chrome/browser/tabs/tab_strip_model_unittest.cc4
-rw-r--r--chrome/browser/views/tabs/dragged_tab_controller.cc499
-rw-r--r--chrome/browser/views/tabs/dragged_tab_controller.h20
-rw-r--r--chrome/browser/views/tabs/dragged_tab_view.cc6
12 files changed, 1079 insertions, 107 deletions
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index 49333b6..375f566 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -22,6 +22,7 @@
#include "chrome/browser/cert_store.h"
#include "chrome/browser/character_encoding.h"
#include "chrome/browser/debugger/debugger_window.h"
+#include "chrome/browser/dock_info.h"
#include "chrome/browser/dom_ui/new_tab_ui.h"
#include "chrome/browser/download/save_package.h"
#include "chrome/browser/history_tab_ui.h"
@@ -1196,12 +1197,19 @@ GURL Browser::GetBlankTabURL() const {
}
void Browser::CreateNewStripWithContents(TabContents* detached_contents,
- const gfx::Rect& window_bounds) {
+ const gfx::Rect& window_bounds,
+ const DockInfo& dock_info) {
DCHECK(type_ == TYPE_NORMAL);
+ gfx::Rect new_window_bounds = window_bounds;
+ bool maximize = false;
+ if (dock_info.GetNewWindowBounds(&new_window_bounds, &maximize))
+ dock_info.AdjustOtherWindowBounds();
+
// Create an empty new browser window the same size as the old one.
Browser* browser = new Browser(TYPE_NORMAL, profile_);
- browser->set_override_bounds(window_bounds);
+ browser->set_override_bounds(new_window_bounds);
+ browser->set_override_maximized(maximize);
browser->CreateBrowserWindow();
browser->tabstrip_model()->AppendTabContents(detached_contents, true);
browser->window()->Show();
diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h
index 4a77152..bb88bc5 100644
--- a/chrome/browser/browser.h
+++ b/chrome/browser/browser.h
@@ -301,7 +301,8 @@ class Browser : public TabStripModelDelegate,
// Overridden from TabStripModelDelegate:
virtual GURL GetBlankTabURL() const;
virtual void CreateNewStripWithContents(TabContents* detached_contents,
- const gfx::Rect& window_bounds);
+ const gfx::Rect& window_bounds,
+ const DockInfo& dock_info);
virtual int GetDragActions() const;
// Construct a TabContents for a given URL, profile and transition type.
// If instance is not null, its process will be used to render the tab.
diff --git a/chrome/browser/browser.scons b/chrome/browser/browser.scons
index 202945f..a4213e3 100644
--- a/chrome/browser/browser.scons
+++ b/chrome/browser/browser.scons
@@ -153,6 +153,7 @@ if env['PLATFORM'] == 'win32':
'chrome_plugin_browsing_context.cc',
'chrome_plugin_host.cc',
'controller.cc',
+ 'dock_info.cc',
'dom_ui/chrome_url_data_manager.cc',
'dom_ui/dom_ui_host.cc',
'dom_ui/html_dialog_contents.cc',
diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj
index 3a791a3..f900e92 100644
--- a/chrome/browser/browser.vcproj
+++ b/chrome/browser/browser.vcproj
@@ -998,6 +998,14 @@
>
</File>
<File
+ RelativePath=".\dock_info.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\dock_info.h"
+ >
+ </File>
+ <File
RelativePath=".\find_notification_details.h"
>
</File>
diff --git a/chrome/browser/dock_info.cc b/chrome/browser/dock_info.cc
new file mode 100644
index 0000000..fdf9f57
--- /dev/null
+++ b/chrome/browser/dock_info.cc
@@ -0,0 +1,481 @@
+// Copyright (c) 2006-2008 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/dock_info.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "chrome/browser/views/frame/browser_view.h"
+
+namespace {
+
+// Distance in pixels between the hotspot and when the hint should be shown.
+const int kHotSpotDeltaX = 100;
+const int kHotSpotDeltaY = 80;
+
+// Distance in pixels between the hotspot and when the hint should be shown
+// and enabled.
+const int kEnableDeltaX = 50;
+const int kEnableDeltaY = 37;
+
+// Distance used when maximizing. The maximize area is the whole top of the
+// monitor.
+const int kMaximizeHotSpotDeltaY = 100;
+const int kMaximizeEnableDeltaY = 50;
+
+// Returns true if |screen_loc| is close to the hotspot at |x|, |y|. If the
+// point is close enough to the hotspot true is returned and |in_enable_area|
+// is set appropriately.
+bool IsCloseToPoint(const gfx::Point& screen_loc,
+ int x,
+ int y,
+ bool* in_enable_area) {
+ int delta_x = abs(x - screen_loc.x());
+ int delta_y = abs(y - screen_loc.y());
+ *in_enable_area = (delta_x < kEnableDeltaX && delta_y < kEnableDeltaY);
+ return *in_enable_area || (delta_x < kHotSpotDeltaX &&
+ delta_y < kHotSpotDeltaY);
+}
+
+// Variant of IsCloseToPoint used for monitor relative positions.
+bool IsCloseToMonitorPoint(const gfx::Point& screen_loc,
+ int x,
+ int y,
+ DockInfo::Type type,
+ bool* in_enable_area) {
+ // Because the monitor relative positions are aligned with the edge of the
+ // monitor these need to be handled differently.
+ int delta_x = abs(x - screen_loc.x());
+ int delta_y = abs(y - screen_loc.y());
+
+ int enable_delta_x = kEnableDeltaX;
+ int enable_delta_y = kEnableDeltaY;
+ int hot_spot_delta_x = kHotSpotDeltaX;
+ int hot_spot_delta_y = kHotSpotDeltaY;
+
+ switch (type) {
+ case DockInfo::LEFT_HALF:
+ case DockInfo::RIGHT_HALF:
+ enable_delta_x += enable_delta_x;
+ hot_spot_delta_x += hot_spot_delta_x;
+ break;
+
+
+ case DockInfo::MAXIMIZE:
+ case DockInfo::BOTTOM_HALF:
+ enable_delta_y += enable_delta_y;
+ hot_spot_delta_y += hot_spot_delta_y;
+ break;
+
+ default:
+ NOTREACHED();
+ return false;
+ }
+ *in_enable_area = (delta_x < enable_delta_x && delta_y < enable_delta_y);
+ bool result = (*in_enable_area || (delta_x < hot_spot_delta_x &&
+ delta_y < hot_spot_delta_y));
+ if (type != DockInfo::MAXIMIZE)
+ return result;
+
+ // Make the maximize area the whole top of the monitor.
+ int max_delta_y = abs(screen_loc.y() - y);
+ *in_enable_area = (*in_enable_area || (max_delta_y < kMaximizeEnableDeltaY));
+ return *in_enable_area || (max_delta_y < kMaximizeHotSpotDeltaY);
+}
+
+// BaseWindowFinder -----------------------------------------------------------
+
+// Base class used to locate a window. This is intended to be used with the
+// various win32 functions that iterate over windows.
+//
+// A subclass need only override ShouldStopIterating to determine when
+// iteration should stop.
+class BaseWindowFinder {
+ public:
+ // Creates a BaseWindowFinder with the specified set of HWNDs to ignore.
+ BaseWindowFinder(const std::set<HWND>& ignore) : ignore_(ignore) {}
+ virtual ~BaseWindowFinder() {}
+
+ protected:
+ // Returns true if iteration should stop, false if iteration should continue.
+ virtual bool ShouldStopIterating(HWND window) = 0;
+
+ static BOOL CALLBACK WindowCallbackProc(HWND hwnd, LPARAM lParam) {
+ BaseWindowFinder* finder = reinterpret_cast<BaseWindowFinder*>(lParam);
+ if (finder->ignore_.find(hwnd) != finder->ignore_.end())
+ return TRUE;
+
+ return finder->ShouldStopIterating(hwnd) ? FALSE : TRUE;
+ }
+
+ private:
+ const std::set<HWND>& ignore_;
+
+ DISALLOW_COPY_AND_ASSIGN(BaseWindowFinder);
+};
+
+// TopMostFinder --------------------------------------------------------------
+
+// Helper class to determine if a particular point of a window is not obscured
+// by another window.
+class TopMostFinder : public BaseWindowFinder {
+ public:
+ // Returns true if |window| is the topmost window at the location
+ // |screen_loc|, not including the windows in |ignore|.
+ static bool IsTopMostWindowAtPoint(HWND window,
+ const gfx::Point& screen_loc,
+ const std::set<HWND>& ignore) {
+ TopMostFinder finder(window, screen_loc, ignore);
+ return finder.is_top_most_;
+ }
+
+ virtual bool ShouldStopIterating(HWND hwnd) {
+ if (hwnd == target_) {
+ // Window is topmost, stop iterating.
+ is_top_most_ = true;
+ return true;
+ }
+
+ if (::IsWindowVisible(hwnd)) {
+ CRect r;
+ if (::GetWindowRect(hwnd, &r) && r.PtInRect(screen_loc_.ToPOINT())) {
+ // Not the topmost, stop iterating.
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private:
+ TopMostFinder(HWND window,
+ const gfx::Point& screen_loc,
+ const std::set<HWND>& ignore)
+ : BaseWindowFinder(ignore),
+ target_(window),
+ screen_loc_(screen_loc),
+ is_top_most_(false) {
+ EnumWindows(WindowCallbackProc, reinterpret_cast<LPARAM>(this));
+ }
+
+ // The window we're looking for.
+ HWND target_;
+
+ // Location of window to find.
+ gfx::Point screen_loc_;
+
+ // Is target_ the top most window? This is initially false but set to true
+ // in ShouldStopIterating if target_ is passed in.
+ bool is_top_most_;
+
+ DISALLOW_COPY_AND_ASSIGN(TopMostFinder);
+};
+
+// WindowFinder ---------------------------------------------------------------
+
+// Helper class to determine if a particular point contains a window from our
+// process.
+class LocalProcessWindowFinder : public BaseWindowFinder {
+ public:
+ // Returns the hwnd from our process at screen_loc that is not obscured by
+ // another window. Returns NULL otherwise.
+ static HWND GetProcessWindowAtPoint(const gfx::Point& screen_loc,
+ const std::set<HWND>& ignore) {
+ LocalProcessWindowFinder finder(screen_loc, ignore);
+ if (finder.result_ &&
+ TopMostFinder::IsTopMostWindowAtPoint(finder.result_, screen_loc,
+ ignore)) {
+ return finder.result_;
+ }
+ return NULL;
+ }
+
+ protected:
+ virtual bool ShouldStopIterating(HWND hwnd) {
+ CRect r;
+ if (::IsWindowVisible(hwnd) && ::GetWindowRect(hwnd, &r) &&
+ r.PtInRect(screen_loc_.ToPOINT())) {
+ result_ = hwnd;
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ LocalProcessWindowFinder(const gfx::Point& screen_loc,
+ const std::set<HWND>& ignore)
+ : BaseWindowFinder(ignore),
+ screen_loc_(screen_loc),
+ result_(NULL) {
+ EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc,
+ reinterpret_cast<LPARAM>(this));
+ }
+
+ // Position of the mouse.
+ gfx::Point screen_loc_;
+
+ // The resulting window. This is initially null but set to true in
+ // ShouldStopIterating if an appropriate window is found.
+ HWND result_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalProcessWindowFinder);
+};
+
+// DockToWindowFinder ---------------------------------------------------------
+
+// Helper class for creating a DockInfo from a specified point.
+class DockToWindowFinder : public BaseWindowFinder {
+ public:
+ // Returns the DockInfo for the specified point. If there is no docking
+ // position for the specified point the returned DockInfo has a type of NONE.
+ static DockInfo GetDockInfoAtPoint(const gfx::Point& screen_loc,
+ const std::set<HWND>& ignore) {
+ DockToWindowFinder finder(screen_loc, ignore);
+ if (!finder.result_.hwnd() ||
+ !TopMostFinder::IsTopMostWindowAtPoint(finder.result_.hwnd(),
+ finder.result_.hot_spot(),
+ ignore)) {
+ return DockInfo();
+ }
+ return finder.result_;
+ }
+
+ protected:
+ virtual bool ShouldStopIterating(HWND hwnd) {
+ BrowserWindow* window = BrowserView::GetBrowserWindowForHWND(hwnd);
+ CRect bounds;
+ if (!window || !::IsWindowVisible(hwnd) ||
+ !::GetWindowRect(hwnd, &bounds)) {
+ return false;
+ }
+
+ // Check the three corners we allow docking to. We don't currently allow
+ // docking to top of window as it conflicts with docking to the tab strip.
+ if (CheckPoint(hwnd, bounds.left, (bounds.top + bounds.bottom) / 2,
+ DockInfo::LEFT_OF_WINDOW) ||
+ CheckPoint(hwnd, bounds.right - 1, (bounds.top + bounds.bottom) / 2,
+ DockInfo::RIGHT_OF_WINDOW) ||
+ CheckPoint(hwnd, (bounds.left + bounds.right) / 2, bounds.bottom - 1,
+ DockInfo::BOTTOM_OF_WINDOW)) {
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ DockToWindowFinder(const gfx::Point& screen_loc,
+ const std::set<HWND>& ignore)
+ : BaseWindowFinder(ignore),
+ screen_loc_(screen_loc) {
+ EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc,
+ reinterpret_cast<LPARAM>(this));
+ }
+
+ bool CheckPoint(HWND hwnd, int x, int y, DockInfo::Type type) {
+ bool in_enable_area;
+ if (IsCloseToPoint(screen_loc_, x, y, &in_enable_area)) {
+ result_.set_in_enable_area(in_enable_area);
+ result_.set_hwnd(hwnd);
+ result_.set_type(type);
+ result_.set_hot_spot(gfx::Point(x, y));
+ return true;
+ }
+ return false;
+ }
+
+ // The location to look for.
+ gfx::Point screen_loc_;
+
+ // The resulting DockInfo.
+ DockInfo result_;
+
+ DISALLOW_COPY_AND_ASSIGN(DockToWindowFinder);
+};
+
+} // namespace
+
+// DockInfo -------------------------------------------------------------------
+
+DockInfo DockInfo::GetDockInfoAtPoint(const gfx::Point& screen_point,
+ const std::set<HWND>& ignore) {
+ // Try docking to a window first.
+ DockInfo info = DockToWindowFinder::GetDockInfoAtPoint(screen_point, ignore);
+
+ HMONITOR monitor = MonitorFromPoint(screen_point.ToPOINT(),
+ MONITOR_DEFAULTTONULL);
+ MONITORINFO monitor_info = {0};
+ monitor_info.cbSize = sizeof(MONITORINFO);
+ if (!monitor || !GetMonitorInfo(monitor, &monitor_info)) {
+ info.type_ = NONE;
+ return info;
+ }
+ info.monitor_bounds_ = gfx::Rect(monitor_info.rcWork);
+
+ if (info.type() != DockInfo::NONE)
+ return info;
+
+ // No window relative positions. Try monitor relative positions.
+ RECT& m_bounds = monitor_info.rcWork;
+ int mid_x = (m_bounds.left + m_bounds.right) / 2;
+ int mid_y = (m_bounds.top + m_bounds.bottom) / 2;
+
+ bool result =
+ info.CheckMonitorPoint(screen_point, mid_x, m_bounds.top,
+ DockInfo::MAXIMIZE) ||
+ info.CheckMonitorPoint(screen_point, mid_x, m_bounds.bottom,
+ DockInfo::BOTTOM_HALF) ||
+ info.CheckMonitorPoint(screen_point, m_bounds.left, mid_y,
+ DockInfo::LEFT_HALF) ||
+ info.CheckMonitorPoint(screen_point, m_bounds.right, mid_y,
+ DockInfo::RIGHT_HALF);
+
+ return info;
+}
+
+HWND DockInfo::GetLocalProcessWindowAtPoint(const gfx::Point& screen_point,
+ const std::set<HWND>& ignore) {
+ return
+ LocalProcessWindowFinder::GetProcessWindowAtPoint(screen_point, ignore);
+}
+
+bool DockInfo::IsValidForPoint(const gfx::Point& screen_point) {
+ if (type_ == NONE)
+ return false;
+
+ if (hwnd_) {
+ return IsCloseToPoint(screen_point, hot_spot_.x(), hot_spot_.y(),
+ &in_enable_area_);
+ }
+
+ return monitor_bounds_.Contains(screen_point) &&
+ IsCloseToMonitorPoint(screen_point, hot_spot_.x(),
+ hot_spot_.y(), type_, &in_enable_area_);
+}
+
+bool DockInfo::GetNewWindowBounds(gfx::Rect* new_window_bounds,
+ bool* maximize_new_window) const {
+ if (type_ == NONE || !in_enable_area_)
+ return false;
+
+ RECT window_rect;
+ if (hwnd_ && !GetWindowRect(hwnd_, &window_rect))
+ return false;
+
+ int half_m_width = (monitor_bounds_.right() - monitor_bounds_.x()) / 2;
+ int half_m_height = (monitor_bounds_.bottom() - monitor_bounds_.y()) / 2;
+ bool unmaximize_other_window = false;
+
+ *maximize_new_window = false;
+
+ switch (type_) {
+ case LEFT_OF_WINDOW:
+ new_window_bounds->SetRect(monitor_bounds_.x(), window_rect.top,
+ half_m_width, window_rect.bottom - window_rect.top);
+ break;
+
+ case RIGHT_OF_WINDOW:
+ new_window_bounds->SetRect(monitor_bounds_.x() + half_m_width,
+ window_rect.top, half_m_width, window_rect.bottom - window_rect.top);
+ break;
+
+ case TOP_OF_WINDOW:
+ new_window_bounds->SetRect(window_rect.left, monitor_bounds_.y(),
+ window_rect.right - window_rect.left,
+ half_m_height);
+ break;
+
+ case BOTTOM_OF_WINDOW:
+ new_window_bounds->SetRect(window_rect.left,
+ monitor_bounds_.y() + half_m_height,
+ window_rect.right - window_rect.left,
+ half_m_height);
+ break;
+
+ case LEFT_HALF:
+ new_window_bounds->SetRect(monitor_bounds_.x(), monitor_bounds_.y(),
+ half_m_width, monitor_bounds_.height());
+ break;
+
+ case RIGHT_HALF:
+ new_window_bounds->SetRect(monitor_bounds_.right() - half_m_width,
+ monitor_bounds_.y(), half_m_width, monitor_bounds_.height());
+ break;
+
+ case BOTTOM_HALF:
+ new_window_bounds->SetRect(monitor_bounds_.x(),
+ monitor_bounds_.y() + half_m_height,
+ monitor_bounds_.width(), half_m_height);
+ break;
+
+ case MAXIMIZE:
+ *maximize_new_window = true;
+ break;
+
+ default:
+ NOTREACHED();
+ }
+ return true;
+}
+
+void DockInfo::AdjustOtherWindowBounds() const {
+ if (!in_enable_area_)
+ return;
+
+ RECT window_rect;
+ if (!hwnd_ || !GetWindowRect(hwnd_, &window_rect))
+ return;
+
+ gfx::Rect other_window_bounds;
+ int half_m_width = (monitor_bounds_.right() - monitor_bounds_.x()) / 2;
+ int half_m_height = (monitor_bounds_.bottom() - monitor_bounds_.y()) / 2;
+
+ switch (type_) {
+ case LEFT_OF_WINDOW:
+ other_window_bounds.SetRect(monitor_bounds_.x() + half_m_width,
+ window_rect.top, half_m_width, window_rect.bottom - window_rect.top);
+ break;
+
+ case RIGHT_OF_WINDOW:
+ other_window_bounds.SetRect(monitor_bounds_.x(), window_rect.top,
+ half_m_width, window_rect.bottom - window_rect.top);
+ break;
+
+ case TOP_OF_WINDOW:
+ other_window_bounds.SetRect(window_rect.left,
+ monitor_bounds_.y() + half_m_height,
+ window_rect.right - window_rect.left,
+ half_m_height);
+ break;
+
+ case BOTTOM_OF_WINDOW:
+ other_window_bounds.SetRect(window_rect.left, monitor_bounds_.y(),
+ window_rect.right - window_rect.left,
+ half_m_height);
+ break;
+
+ default:
+ return;
+ }
+
+ if (IsZoomed(hwnd_)) {
+ // We're docking relative to another window, we need to make sure the
+ // window we're docking to isn't maximized.
+ ShowWindow(hwnd_, SW_RESTORE | SW_SHOWNA);
+ }
+ ::SetWindowPos(hwnd_, HWND_TOP, other_window_bounds.x(),
+ other_window_bounds.y(), other_window_bounds.width(),
+ other_window_bounds.height(),
+ SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
+}
+
+bool DockInfo::CheckMonitorPoint(const gfx::Point& screen_loc,
+ int x,
+ int y,
+ Type type) {
+ if (IsCloseToMonitorPoint(screen_loc, x, y, type, &in_enable_area_)) {
+ hot_spot_.SetPoint(x, y);
+ type_ = type;
+ return true;
+ }
+ return false;
+}
diff --git a/chrome/browser/dock_info.h b/chrome/browser/dock_info.h
new file mode 100644
index 0000000..819322e
--- /dev/null
+++ b/chrome/browser/dock_info.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2006-2008 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 CHROME_BROWSER_DOCK_INFO_H_
+#define CHROME_BROWSER_DOCK_INFO_H_
+
+#include <set>
+#include <windows.h>
+
+#include "base/gfx/point.h"
+#include "base/gfx/rect.h"
+
+class Profile;
+
+// DockInfo is used to do determine possible dock locations for a dragged
+// tab. To use DockInfo invoke GetDockInfoAtPoint. This returns a new
+// DockInfo whose type indicates the type of dock that should occur based
+// on the screen location. As the user drags the mouse around invoke
+// IsValidForPoint, this returns true if the DockInfo is still valid for the
+// new location. If the DockInfo is not valid, invoke GetDockInfoAtPoint to
+// get the new DockInfo. Use GetNewWindowBounds to get the position to place
+// the new window at.
+//
+// DockInfos are cheap and explicitly allow copy and assignment operators.
+class DockInfo {
+ public:
+ // Possible dock positions.
+ enum Type {
+ // Indicates there is no valid dock position for the current location.
+ NONE,
+
+ // Indicates the new window should be positioned relative to the window
+ // identified by hwnd().
+ LEFT_OF_WINDOW,
+ RIGHT_OF_WINDOW,
+ BOTTOM_OF_WINDOW,
+ TOP_OF_WINDOW,
+
+ // Indicates the window should be maximized on the monitor at hot_spot.
+ MAXIMIZE,
+
+ // Indicates the window should be docked to a specific side of the monitor.
+ LEFT_HALF,
+ RIGHT_HALF,
+ BOTTOM_HALF
+ };
+
+ DockInfo() : type_(NONE), hwnd_(NULL), in_enable_area_(false) {}
+
+ // Returns the DockInfo for the specified point |screen_point|. |ignore|
+ // contains the set of hwnds to ignore from consideration. This contains the
+ // dragged window as well as any windows showing possible dock locations.
+ //
+ // If there is no docking position for the specified location the returned
+ // DockInfo has a type of NONE.
+ static DockInfo GetDockInfoAtPoint(const gfx::Point& screen_point,
+ const std::set<HWND>& ignore);
+
+ // Returns the top most window from the current process at |screen_point|.
+ // See GetDockInfoAtPoint for a description of |ignore|. This returns NULL if
+ // there is no window from the current process at |screen_point|, or another
+ // window obscures the topmost window from our process at |screen_point|.
+ static HWND GetLocalProcessWindowAtPoint(const gfx::Point& screen_point,
+ const std::set<HWND>& ignore);
+
+ // Returns true if this DockInfo is valid for the specified point. This
+ // resets in_enable_area based on the new location.
+ bool IsValidForPoint(const gfx::Point& screen_point);
+
+ // Returns the bounds for the new window in |new_window_bounds|. If the new
+ // window is to be maximized, |maximize_new_window| is set to true.
+ // This returns true if type is other than NONE or the mouse isn't in the
+ // enable area, false otherwise.
+ bool GetNewWindowBounds(gfx::Rect* new_window_bounds,
+ bool* maximize_new_window) const;
+
+ // Adjust the bounds of the other window during docking. Does nothing if type
+ // is NONE, in_enable_are is false, or the type is not window relative.
+ void AdjustOtherWindowBounds() const;
+
+ // Type of docking to occur.
+ void set_type(Type type) { type_ = type; }
+ Type type() const { return type_; }
+
+ // The window to dock too. Is null for dock types that are relative to the
+ // monitor.
+ void set_hwnd(HWND hwnd) { hwnd_ = hwnd; }
+ HWND hwnd() const { return hwnd_; }
+
+ // The location of the hotspot.
+ void set_hot_spot(const gfx::Point& hot_spot) { hot_spot_ = hot_spot; }
+ const gfx::Point& hot_spot() const { return hot_spot_; }
+
+ // Bounds of the monitor.
+ void set_monitor_bounds(const gfx::Rect& monitor_bounds) {
+ monitor_bounds_ = monitor_bounds;
+ }
+ const gfx::Rect& monitor_bounds() const { return monitor_bounds_; }
+
+ // Returns true if the drop should result in docking. DockInfo maintains two
+ // states (as indicated by this boolean):
+ // 1. The mouse is close enough to the hot spot such that a visual indicator
+ // should be shown, but if the user releases the mouse docking shouldn't
+ // result. This corresponds to a value of false for in_enable_area.
+ // 2. The mouse is close enough to the hot spot such that releasing the mouse
+ // should result in docking. This corresponds to a value of true for
+ // in_enable_area.
+ void set_in_enable_area(bool in_enable_area) {
+ in_enable_area_ = in_enable_area;
+ }
+ bool in_enable_area() const { return in_enable_area_; }
+
+ // Returns true if |other| is considered equal to this. Two DockInfos are
+ // considered equal if they have the same type and same hwnd.
+ bool equals(const DockInfo& other) const {
+ return type_ == other.type_ && hwnd_ == other.hwnd_;
+ }
+
+ // If screen_loc is close enough to the hot spot given by |x| and |y|, the
+ // type and hot_spot are set from the supplied parameters. This is used
+ // internally, there is no need to invoke this otherwise.
+ bool CheckMonitorPoint(const gfx::Point& screen_loc,
+ int x,
+ int y,
+ Type type);
+
+ private:
+ Type type_;
+ HWND hwnd_;
+ gfx::Point hot_spot_;
+ gfx::Rect monitor_bounds_;
+ bool in_enable_area_;
+};
+
+#endif // CHROME_BROWSER_DOCK_INFO_H_
diff --git a/chrome/browser/tabs/tab_strip_model.cc b/chrome/browser/tabs/tab_strip_model.cc
index 176bf94..21ba9b5 100644
--- a/chrome/browser/tabs/tab_strip_model.cc
+++ b/chrome/browser/tabs/tab_strip_model.cc
@@ -401,9 +401,11 @@ void TabStripModel::SelectLastTab() {
}
void TabStripModel::TearOffTabContents(TabContents* detached_contents,
- const gfx::Rect& window_bounds) {
+ const gfx::Rect& window_bounds,
+ const DockInfo& dock_info) {
DCHECK(detached_contents);
- delegate_->CreateNewStripWithContents(detached_contents, window_bounds);
+ delegate_->CreateNewStripWithContents(detached_contents, window_bounds,
+ dock_info);
}
// Context menu functions.
diff --git a/chrome/browser/tabs/tab_strip_model.h b/chrome/browser/tabs/tab_strip_model.h
index 386599c..d5d56f2 100644
--- a/chrome/browser/tabs/tab_strip_model.h
+++ b/chrome/browser/tabs/tab_strip_model.h
@@ -18,6 +18,7 @@
namespace gfx {
class Point;
}
+class DockInfo;
class GURL;
class NavigationController;
class Profile;
@@ -94,8 +95,11 @@ class TabStripModelDelegate {
// Ask for a new TabStripModel to be created and the given tab contents to
// be added to it. Its size and position are reflected in |window_bounds|.
+ // If |dock_info|'s type is other than NONE, the newly created window should
+ // be docked as identified by |dock_info|.
virtual void CreateNewStripWithContents(TabContents* contents,
- const gfx::Rect& window_bounds) = 0;
+ const gfx::Rect& window_bounds,
+ const DockInfo& dock_info) = 0;
enum {
TAB_MOVE_ACTION = 1,
@@ -351,7 +355,8 @@ class TabStripModel : public NotificationObserver {
// The specified contents should be opened in a new tabstrip.
void TearOffTabContents(TabContents* detached_contents,
- const gfx::Rect& window_bounds);
+ const gfx::Rect& window_bounds,
+ const DockInfo& dock_info);
// Context menu functions.
enum ContextMenuCommand {
@@ -517,4 +522,3 @@ class TabStripModel : public NotificationObserver {
};
#endif // CHROME_BROWSER_TABS_TAB_STRIP_MODEL_H__
-
diff --git a/chrome/browser/tabs/tab_strip_model_unittest.cc b/chrome/browser/tabs/tab_strip_model_unittest.cc
index 24c9b3c..071975f 100644
--- a/chrome/browser/tabs/tab_strip_model_unittest.cc
+++ b/chrome/browser/tabs/tab_strip_model_unittest.cc
@@ -4,6 +4,7 @@
#include "base/file_util.h"
#include "base/path_service.h"
+#include "chrome/browser/dock_info.h"
#include "chrome/browser/dom_ui/new_tab_ui.h"
#include "chrome/browser/navigation_controller.h"
#include "chrome/browser/navigation_entry.h"
@@ -994,7 +995,8 @@ class TabStripDummyDelegate : public TabStripModelDelegate {
// Overridden from TabStripModelDelegate:
virtual GURL GetBlankTabURL() const { return NewTabUIURL(); }
virtual void CreateNewStripWithContents(TabContents* contents,
- const gfx::Rect& window_bounds) {}
+ const gfx::Rect& window_bounds,
+ const DockInfo& dock_info) {}
virtual int GetDragActions() const { return 0; }
virtual TabContents* CreateTabContentsForURL(
const GURL& url,
diff --git a/chrome/browser/views/tabs/dragged_tab_controller.cc b/chrome/browser/views/tabs/dragged_tab_controller.cc
index f81fc84..c88920a 100644
--- a/chrome/browser/views/tabs/dragged_tab_controller.cc
+++ b/chrome/browser/views/tabs/dragged_tab_controller.cc
@@ -3,17 +3,20 @@
// found in the LICENSE file.
#include <math.h>
+#include <set>
#include "chrome/browser/views/tabs/dragged_tab_controller.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/tab_contents.h"
+#include "chrome/browser/user_metrics.h"
#include "chrome/browser/views/frame/browser_view.h"
#include "chrome/browser/views/tabs/dragged_tab_view.h"
#include "chrome/browser/views/tabs/hwnd_photobooth.h"
#include "chrome/browser/views/tabs/tab.h"
#include "chrome/browser/views/tabs/tab_strip.h"
#include "chrome/browser/web_contents.h"
+#include "chrome/common/animation.h"
#include "chrome/views/event.h"
#include "chrome/views/root_view.h"
#include "skia/include/SkBitmap.h"
@@ -22,95 +25,138 @@ static const int kHorizontalMoveThreshold = 16; // pixels
namespace {
-///////////////////////////////////////////////////////////////////////////////
-// WindowFinder
-// A helper class that finds the topmost window from our thread under a
-// particular point. This returns NULL if we don't have a window at the
-// specified point, or there is another window from another app on top of our
-// window at the specified point.
-//
-class WindowFinder {
- public:
- static HWND WindowForPoint(const gfx::Point& screen_point, HWND ignore1) {
- WindowFinder instance(screen_point, ignore1);
- return instance.GetResult();
- }
+// Horizontal width of DockView. The height is 3/4 of this. If you change this,
+// be sure and update the constants in DockInfo (kEnableDeltaX/kEnableDeltaY).
+const int kDropWindowSize = 100;
- private:
- WindowFinder(const gfx::Point& screen_point, HWND ignore1)
- : screen_point_(screen_point.ToPOINT()),
- ignore1_(ignore1),
- result1_(NULL),
- result2_(NULL) {
- }
-
- static BOOL CALLBACK EnumThreadWindowsProc(HWND hwnd, LPARAM lParam) {
- WindowFinder* wf = reinterpret_cast<WindowFinder*>(lParam);
- if (hwnd == wf->ignore1_)
- return TRUE;
-
- if (::IsWindowVisible(hwnd)) {
- CRect r;
- ::GetWindowRect(hwnd, &r);
- if (r.PtInRect(wf->screen_point_)) {
- // We always deal with the root HWND.
- wf->result1_ = GetAncestor(hwnd, GA_ROOT);
- return FALSE;
- }
- }
- return TRUE;
+// TODO (glen): nuke this class in favor of something pretty. Consider this
+// class a placeholder for the real thing.
+class DockView : public views::View {
+ public:
+ explicit DockView(DockInfo::Type type)
+ : size_(kDropWindowSize),
+ rect_radius_(4),
+ stroke_size_(4),
+ inner_stroke_size_(2),
+ inner_margin_(8),
+ inner_padding_(8),
+ type_(type) {}
+
+ virtual gfx::Size GetPreferredSize() {
+ return gfx::Size(size_, size_);
}
- static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
- WindowFinder* wf = reinterpret_cast<WindowFinder*>(lParam);
- if (hwnd == wf->ignore1_)
- return TRUE;
-
- if (hwnd == wf->result1_) {
- // Result from first pass is the topmost window under point. Use it.
- wf->result2_ = hwnd;
- return FALSE;
+ virtual void PaintBackground(ChromeCanvas* canvas) {
+ int h = size_ * 3 / 4;
+ int outer_x = (width() - size_) / 2;
+ int outer_y = (height() - h) / 2;
+ switch (type_) {
+ case DockInfo::MAXIMIZE:
+ outer_y = 0;
+ break;
+ case DockInfo::LEFT_HALF:
+ outer_x = 0;
+ break;
+ case DockInfo::RIGHT_HALF:
+ outer_x = width() - size_;
+ break;
+ case DockInfo::BOTTOM_HALF:
+ outer_y = height() - h;
+ break;
+ default:
+ break;
}
- if (::IsWindowVisible(hwnd)) {
- CRect r;
- if (::GetWindowRect(hwnd, &r) && r.PtInRect(wf->screen_point_)) {
- // Result from first pass is not the topmost window under point.
- return FALSE;
- }
- }
- return TRUE; // Keep iterating.
- }
-
- HWND GetResult() {
- // We take a two step approach to find the topmost window under point.
- // Step 1: find the topmost window in our thread under point.
- EnumThreadWindows(GetCurrentThreadId(), EnumThreadWindowsProc,
- reinterpret_cast<LPARAM>(this));
- if (result1_) {
- // Step 2.
- // We have a window under the point in our thread. Make sure there isn't
- // another window from another app on top of our window at point.
- // NOTE: EnumWindows iterates window from topmost window to bottommost
- // window.
- EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(this));
+ SkRect outer_rect = { SkIntToScalar(outer_x),
+ SkIntToScalar(outer_y),
+ SkIntToScalar(outer_x + size_),
+ SkIntToScalar(outer_y + h) };
+
+ // Fill the background rect.
+ SkPaint paint;
+ paint.setColor(SkColorSetRGB(58, 58, 58));
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawRoundRect(outer_rect, SkIntToScalar(rect_radius_),
+ SkIntToScalar(rect_radius_), paint);
+
+ // Outline the background rect.
+ paint.setFlags(SkPaint::kAntiAlias_Flag);
+ paint.setStrokeWidth(SkIntToScalar(stroke_size_));
+ paint.setColor(SK_ColorBLACK);
+ paint.setStyle(SkPaint::kStroke_Style);
+ canvas->drawRoundRect(outer_rect, SkIntToScalar(rect_radius_),
+ SkIntToScalar(rect_radius_), paint);
+
+ // Then the inner rect.
+ int inner_x = outer_x + inner_margin_;
+ int inner_y = outer_y + inner_margin_;
+ int inner_width =
+ (size_ - inner_margin_ - inner_margin_ - inner_padding_) / 2;
+ int inner_height = (h - inner_margin_ - inner_margin_);
+ switch (type_) {
+ case DockInfo::LEFT_OF_WINDOW:
+ case DockInfo::RIGHT_OF_WINDOW:
+ DrawWindow(canvas, inner_x, inner_y, inner_width, inner_height);
+ DrawWindow(canvas, inner_x + inner_width + inner_padding_, inner_y,
+ inner_width, inner_height);
+ break;
+
+ case DockInfo::TOP_OF_WINDOW:
+ case DockInfo::BOTTOM_OF_WINDOW:
+ inner_height =
+ (h - inner_margin_ - inner_margin_ - inner_padding_) / 2;
+ inner_width += inner_width + inner_padding_;
+ DrawWindow(canvas, inner_x, inner_y, inner_width, inner_height);
+ DrawWindow(canvas, inner_x, inner_y + inner_height + inner_padding_,
+ inner_width, inner_height);
+ break;
+
+ case DockInfo::MAXIMIZE:
+ inner_width += inner_width + inner_padding_;
+ DrawWindow(canvas, inner_x, inner_y, inner_width, inner_height);
+ break;
+
+ case DockInfo::LEFT_HALF:
+ DrawWindow(canvas, inner_x, inner_y, inner_width, inner_height);
+ break;
+
+ case DockInfo::RIGHT_HALF:
+ DrawWindow(canvas, inner_x + inner_width + inner_padding_, inner_y,
+ inner_width, inner_height);
+ break;
+
+ case DockInfo::BOTTOM_HALF:
+ inner_height =
+ (h - inner_margin_ - inner_margin_ - inner_padding_) / 2;
+ inner_width += inner_width + inner_padding_;
+ DrawWindow(canvas, inner_x, inner_y + inner_height + inner_padding_,
+ inner_width, inner_height);
+ break;
}
- return result2_;
}
- // Location we're looking for.
- POINT screen_point_;
-
- // HWND to ignore.
- HWND ignore1_;
-
- // Result from first pass. See docs in GetResult for details.
- HWND result1_;
+ private:
+ void DrawWindow(ChromeCanvas* canvas, int x, int y, int w, int h) {
+ canvas->FillRectInt(SkColorSetRGB(160, 160, 160), x, y, w, h);
+
+ SkPaint paint;
+ paint.setStrokeWidth(SkIntToScalar(inner_stroke_size_));
+ paint.setColor(SK_ColorWHITE);
+ paint.setStyle(SkPaint::kStroke_Style);
+ SkRect rect = { SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + w),
+ SkIntToScalar(y + h) };
+ canvas->drawRect(rect, paint);
+ }
- // Result from second pass. See docs in GetResult for details.
- HWND result2_;
+ int size_;
+ int rect_radius_;
+ int stroke_size_;
+ int inner_stroke_size_;
+ int inner_margin_;
+ int inner_padding_;
+ DockInfo::Type type_;
- DISALLOW_EVIL_CONSTRUCTORS(WindowFinder);
+ DISALLOW_COPY_AND_ASSIGN(DockView);
};
gfx::Point ConvertScreenPointToTabStripPoint(TabStrip* tabstrip,
@@ -121,7 +167,139 @@ gfx::Point ConvertScreenPointToTabStripPoint(TabStrip* tabstrip,
screen_point.y() - tabstrip_topleft.y());
}
-}
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// DockDisplayer
+
+// DockDisplayer is responsible for giving the user a visual indication of a
+// possible dock position (as represented by DockInfo). DockDisplayer shows
+// a window with a DockView in it. Two animations are used that correspond to
+// the state of DockInfo::in_enable_area.
+class DraggedTabController::DockDisplayer : public AnimationDelegate {
+ public:
+ DockDisplayer(DraggedTabController* controller,
+ const DockInfo& info)
+ : controller_(controller),
+ popup_(NULL),
+ popup_hwnd_(NULL),
+#pragma warning(suppress: 4355) // Okay to pass "this" here.
+ hot_animation_(this),
+ enable_animation_(this),
+ hidden_(false),
+ in_enable_area_(info.in_enable_area()) {
+ gfx::Rect bounds(info.hot_spot().x() - kDropWindowSize / 2,
+ info.hot_spot().y() - kDropWindowSize / 2,
+ kDropWindowSize, kDropWindowSize);
+ switch (info.type()) {
+ case DockInfo::MAXIMIZE:
+ bounds.Offset(0, kDropWindowSize / 2);
+ break;
+ case DockInfo::LEFT_HALF:
+ bounds.Offset(kDropWindowSize / 2, 0);
+ break;
+ case DockInfo::RIGHT_HALF:
+ bounds.Offset(-kDropWindowSize / 2, 0);
+ break;
+ case DockInfo::BOTTOM_HALF:
+ bounds.Offset(0, -kDropWindowSize / 2);
+ break;
+ default:
+ break;
+ }
+
+ popup_ = new views::ContainerWin;
+ popup_->set_window_style(WS_POPUP);
+ popup_->set_window_ex_style(WS_EX_LAYERED | WS_EX_TOOLWINDOW |
+ WS_EX_TOPMOST);
+ popup_->SetLayeredAlpha(0x00);
+ popup_->Init(NULL, bounds, false);
+ popup_->SetContentsView(new DockView(info.type()));
+ hot_animation_.Show();
+ if (info.in_enable_area())
+ enable_animation_.Show();
+ popup_->SetWindowPos(HWND_TOP, 0, 0, 0, 0,
+ SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOMOVE | SWP_SHOWWINDOW);
+ popup_hwnd_ = popup_->GetHWND();
+ }
+
+ ~DockDisplayer() {
+ if (controller_)
+ controller_->DockDisplayerDestroyed(this);
+ }
+
+ // Updates the state based on |in_enable_area|.
+ void UpdateInEnabledArea(bool in_enable_area) {
+ if (in_enable_area != in_enable_area_) {
+ in_enable_area_ = in_enable_area;
+ if (!in_enable_area_)
+ enable_animation_.Hide();
+ else
+ enable_animation_.Show();
+ }
+ }
+
+ // Resets the reference to the hosting DraggedTabController. This is invoked
+ // when the DraggedTabController is destoryed.
+ void clear_controller() { controller_ = NULL; }
+
+ // HWND of the window we create.
+ HWND popup_hwnd() { return popup_hwnd_; }
+
+ // Starts the hide animation. When the window is closed the
+ // DraggedTabController is notified by way of the DockDisplayerDestroyed
+ // method
+ void Hide() {
+ if (hidden_)
+ return;
+
+ if (!popup_) {
+ delete this;
+ return;
+ }
+ hidden_ = true;
+ enable_animation_.Hide();
+ hot_animation_.Hide();
+ }
+
+ virtual void AnimationProgressed(const Animation* animation) {
+ popup_->SetLayeredAlpha(
+ static_cast<BYTE>((hot_animation_.GetCurrentValue() +
+ enable_animation_.GetCurrentValue()) / 2 * 255.0));
+ popup_->GetRootView()->SchedulePaint();
+ }
+
+ virtual void AnimationEnded(const Animation* animation) {
+ if (!hidden_)
+ return;
+ popup_->Close();
+ delete this;
+ return;
+ }
+
+ private:
+ // DraggedTabController that created us.
+ DraggedTabController* controller_;
+
+ // Window we're showing.
+ views::ContainerWin* popup_;
+
+ // HWND of |popup_|. We cache this to avoid the possibility of invoking a
+ // method on popup_ after we close it.
+ HWND popup_hwnd_;
+
+ // Animation corresponding to !DockInfo::in_enable_area.
+ SlideAnimation hot_animation_;
+
+ // Animation corresponding to DockInfo::in_enable_area.
+ SlideAnimation enable_animation_;
+
+ // Have we been hidden?
+ bool hidden_;
+
+ // Value of DockInfo::in_enable_area.
+ bool in_enable_area_;
+};
///////////////////////////////////////////////////////////////////////////////
// DraggedTabController, public:
@@ -330,10 +508,45 @@ void DraggedTabController::InitWindowCreatePoint() {
gfx::Point DraggedTabController::GetWindowCreatePoint() const {
POINT pt;
GetCursorPos(&pt);
+ if (dock_info_.type() != DockInfo::NONE) {
+ // If we're going to dock, we need to return the exact coordinate,
+ // otherwise we may attempt to maximize on the wrong monitor.
+ return gfx::Point(pt);
+ }
return gfx::Point(pt.x - window_create_point_.x(),
pt.y - window_create_point_.y());
}
+void DraggedTabController::UpdateDockInfo(const gfx::Point& screen_point) {
+ // Update the DockInfo for the current mouse coordinates.
+ DockInfo dock_info = GetDockInfoAtPoint(screen_point);
+ if (!dock_info.equals(dock_info_)) {
+ // DockInfo for current position differs.
+ if (dock_info_.type() != DockInfo::NONE &&
+ !dock_controllers_.empty()) {
+ // Hide old visual indicator.
+ dock_controllers_.back()->Hide();
+ }
+ dock_info_ = dock_info;
+ if (dock_info_.type() != DockInfo::NONE) {
+ // Show new docking position.
+ DockDisplayer* controller = new DockDisplayer(this, dock_info_);
+ if (controller->popup_hwnd()) {
+ dock_controllers_.push_back(controller);
+ dock_windows_.insert(controller->popup_hwnd());
+ } else {
+ delete controller;
+ }
+ }
+ } else if (dock_info_.type() != DockInfo::NONE &&
+ !dock_controllers_.empty()) {
+ // Current dock position is the same as last, update the controller's
+ // in_enable_area state as it may have changed.
+ dock_controllers_.back()->UpdateInEnabledArea(dock_info_.in_enable_area());
+ }
+}
+
+
void DraggedTabController::ChangeDraggedContents(TabContents* new_contents) {
if (dragged_contents_) {
NotificationService::current()->RemoveObserver(this,
@@ -399,6 +612,9 @@ void DraggedTabController::ContinueDragging() {
if (target_tabstrip)
Attach(target_tabstrip, screen_point);
}
+
+ UpdateDockInfo(screen_point);
+
MoveTab(screen_point);
}
@@ -434,21 +650,44 @@ void DraggedTabController::MoveTab(const gfx::Point& screen_point) {
view_->MoveTo(dragged_view_point);
}
+DockInfo DraggedTabController::GetDockInfoAtPoint(
+ const gfx::Point& screen_point) {
+ if (attached_tabstrip_) {
+ // If the mouse is over a tab strip, don't offer a dock position.
+ return DockInfo();
+ }
+
+ if (dock_info_.IsValidForPoint(screen_point)) {
+ // It's possible any given screen coordinate has multiple docking
+ // positions. Check the current info first to avoid having the docking
+ // position bounce around.
+ return dock_info_;
+ }
+
+ HWND dragged_hwnd = view_->GetContainer()->GetHWND();
+ dock_windows_.insert(dragged_hwnd);
+ DockInfo info = DockInfo::GetDockInfoAtPoint(screen_point, dock_windows_);
+ dock_windows_.erase(dragged_hwnd);
+ return info;
+}
+
TabStrip* DraggedTabController::GetTabStripForPoint(
- const gfx::Point& screen_point) const {
+ const gfx::Point& screen_point) {
HWND dragged_hwnd = view_->GetContainer()->GetHWND();
- HWND other_hwnd = WindowFinder::WindowForPoint(screen_point, dragged_hwnd);
- if (!other_hwnd)
+ dock_windows_.insert(dragged_hwnd);
+ HWND local_window =
+ DockInfo::GetLocalProcessWindowAtPoint(screen_point, dock_windows_);
+ dock_windows_.erase(dragged_hwnd);
+ if (!local_window)
+ return NULL;
+ BrowserWindow* browser = BrowserView::GetBrowserWindowForHWND(local_window);
+ if (!browser)
return NULL;
- BrowserWindow* other_frame = BrowserView::GetBrowserWindowForHWND(other_hwnd);
- if (other_frame) {
- TabStrip* other_tabstrip = other_frame->GetTabStrip();
- if (!other_tabstrip->IsCompatibleWith(source_tabstrip_))
- return NULL;
- return GetTabStripIfItContains(other_tabstrip, screen_point);
- }
- return NULL;
+ TabStrip* other_tabstrip = browser->GetTabStrip();
+ if (!other_tabstrip->IsCompatibleWith(source_tabstrip_))
+ return NULL;
+ return GetTabStripIfItContains(other_tabstrip, screen_point);
}
TabStrip* DraggedTabController::GetTabStripIfItContains(
@@ -673,6 +912,16 @@ Tab* DraggedTabController::GetTabMatchingDraggedContents(
}
void DraggedTabController::EndDragImpl(EndDragType type) {
+ // Hide the current dock controllers.
+ for (size_t i = 0; i < dock_controllers_.size(); ++i) {
+ // Be sure and clear the controller first, that way if Hide ends up
+ // deleting the controller it won't call us back.
+ dock_controllers_[i]->clear_controller();
+ dock_controllers_[i]->Hide();
+ }
+ dock_controllers_.clear();
+ dock_windows_.clear();
+
bool destroy_now = true;
if (type != TAB_DESTROYED) {
// We only finish up the drag if we were actually dragging. If we never
@@ -754,14 +1003,61 @@ bool DraggedTabController::CompleteDrag() {
NewCallback(this, &DraggedTabController::OnAnimateToBoundsComplete));
destroy_immediately = false;
} else {
+ if (dock_info_.type() != DockInfo::NONE) {
+ switch (dock_info_.type()) {
+ case DockInfo::LEFT_OF_WINDOW:
+ UserMetrics::RecordAction(L"DockingWindow_Left",
+ source_tabstrip_->model()->profile());
+ break;
+
+ case DockInfo::RIGHT_OF_WINDOW:
+ UserMetrics::RecordAction(L"DockingWindow_Right",
+ source_tabstrip_->model()->profile());
+ break;
+
+ case DockInfo::BOTTOM_OF_WINDOW:
+ UserMetrics::RecordAction(L"DockingWindow_Bottom",
+ source_tabstrip_->model()->profile());
+ break;
+
+ case DockInfo::TOP_OF_WINDOW:
+ UserMetrics::RecordAction(L"DockingWindow_Top",
+ source_tabstrip_->model()->profile());
+ break;
+
+ case DockInfo::MAXIMIZE:
+ UserMetrics::RecordAction(L"DockingWindow_Maximize",
+ source_tabstrip_->model()->profile());
+ break;
+
+ case DockInfo::LEFT_HALF:
+ UserMetrics::RecordAction(L"DockingWindow_LeftHalf",
+ source_tabstrip_->model()->profile());
+ break;
+
+ case DockInfo::RIGHT_HALF:
+ UserMetrics::RecordAction(L"DockingWindow_RightHalf",
+ source_tabstrip_->model()->profile());
+ break;
+
+ case DockInfo::BOTTOM_HALF:
+ UserMetrics::RecordAction(L"DockingWindow_BottomHalf",
+ source_tabstrip_->model()->profile());
+ break;
+
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
// Compel the model to construct a new window for the detached TabContents.
CRect browser_rect;
GetWindowRect(source_tabstrip_->GetContainer()->GetHWND(), &browser_rect);
gfx::Rect window_bounds(
GetWindowCreatePoint(),
gfx::Size(browser_rect.Width(), browser_rect.Height()));
- source_tabstrip_->model()->TearOffTabContents(dragged_contents_,
- window_bounds);
+ source_tabstrip_->model()->delegate()->CreateNewStripWithContents(
+ dragged_contents_, window_bounds, dock_info_);
CleanUpHiddenFrame();
}
@@ -847,3 +1143,20 @@ void DraggedTabController::OnAnimateToBoundsComplete() {
source_tabstrip_->DestroyDragController();
}
+void DraggedTabController::DockDisplayerDestroyed(
+ DockDisplayer* controller) {
+ std::set<HWND>::iterator dock_i =
+ dock_windows_.find(controller->popup_hwnd());
+ if (dock_i != dock_windows_.end())
+ dock_windows_.erase(dock_i);
+ else
+ NOTREACHED();
+
+ std::vector<DockDisplayer*>::iterator i =
+ std::find(dock_controllers_.begin(), dock_controllers_.end(),
+ controller);
+ if (i != dock_controllers_.end())
+ dock_controllers_.erase(i);
+ else
+ NOTREACHED();
+}
diff --git a/chrome/browser/views/tabs/dragged_tab_controller.h b/chrome/browser/views/tabs/dragged_tab_controller.h
index fb367c3..0363d93 100644
--- a/chrome/browser/views/tabs/dragged_tab_controller.h
+++ b/chrome/browser/views/tabs/dragged_tab_controller.h
@@ -7,7 +7,9 @@
#include "base/gfx/rect.h"
#include "base/message_loop.h"
+#include "chrome/browser/dock_info.h"
#include "chrome/browser/tab_contents_delegate.h"
+#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/views/tabs/tab_renderer.h"
#include "chrome/common/notification_service.h"
@@ -15,6 +17,7 @@ namespace views {
class MouseEvent;
class View;
}
+class BrowserWindow;
class DraggedTabView;
class HWNDPhotobooth;
class SkBitmap;
@@ -65,6 +68,9 @@ class DraggedTabController : public TabContentsDelegate,
bool IsDragSourceTab(Tab* tab) const;
private:
+ class DockDisplayer;
+ friend class DockDisplayer;
+
// Enumeration of the ways a drag session can end.
enum EndDragType {
// Drag session exited normally: the user released the mouse.
@@ -118,6 +124,8 @@ class DraggedTabController : public TabContentsDelegate,
// current mouse position.
gfx::Point GetWindowCreatePoint() const;
+ void UpdateDockInfo(const gfx::Point& screen_point);
+
// Replaces the TabContents being dragged with the specified |new_contents|.
// This can occur if the active TabContents for the tab being dragged is
// replaced, e.g. if a transition from one TabContentsType to another occurs
@@ -145,7 +153,9 @@ class DraggedTabController : public TabContentsDelegate,
// Returns the compatible TabStrip that is under the specified point (screen
// coordinates), or NULL if there is none.
- TabStrip* GetTabStripForPoint(const gfx::Point& screen_point) const;
+ TabStrip* GetTabStripForPoint(const gfx::Point& screen_point);
+
+ DockInfo GetDockInfoAtPoint(const gfx::Point& screen_point);
// Returns the specified |tabstrip| if it contains the specified point
// (screen coordinates), NULL if it does not.
@@ -213,6 +223,8 @@ class DraggedTabController : public TabContentsDelegate,
// position.
void OnAnimateToBoundsComplete();
+ void DockDisplayerDestroyed(DockDisplayer* controller);
+
// The TabContents being dragged. This can get replaced during the drag if
// the associated NavigationController is navigated to a different
// TabContentsType.
@@ -279,8 +291,12 @@ class DraggedTabController : public TabContentsDelegate,
// time of the last re-order event.
int last_move_screen_x_;
+ DockInfo dock_info_;
+
+ std::set<HWND> dock_windows_;
+ std::vector<DockDisplayer*> dock_controllers_;
+
DISALLOW_COPY_AND_ASSIGN(DraggedTabController);
};
#endif // CHROME_BROWSER_VIEWS_TABS_DRAGGED_TAB_CONTROLLER_H_
-
diff --git a/chrome/browser/views/tabs/dragged_tab_view.cc b/chrome/browser/views/tabs/dragged_tab_view.cc
index 118f89f..6f20c6a 100644
--- a/chrome/browser/views/tabs/dragged_tab_view.cc
+++ b/chrome/browser/views/tabs/dragged_tab_view.cc
@@ -56,8 +56,7 @@ DraggedTabView::~DraggedTabView() {
}
void DraggedTabView::MoveTo(const gfx::Point& screen_point) {
- if (!container_->IsVisible())
- container_->ShowWindow(SW_SHOWNOACTIVATE);
+ int show_flags = container_->IsVisible() ? SWP_NOZORDER : SWP_SHOWWINDOW;
int x;
if (UILayoutIsRightToLeft() && !attached_) {
@@ -75,7 +74,8 @@ void DraggedTabView::MoveTo(const gfx::Point& screen_point) {
int y = screen_point.y() + mouse_tab_offset_.y() -
ScaleValue(mouse_tab_offset_.y());
- container_->SetWindowPos(NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
+ container_->SetWindowPos(HWND_TOP, x, y, 0, 0,
+ SWP_NOSIZE | SWP_NOACTIVATE | show_flags);
}
void DraggedTabView::Attach(int selected_width) {