summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--chrome/common/slide_animation.h7
-rw-r--r--chrome/common/win_util.cc4
14 files changed, 1085 insertions, 112 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) {
diff --git a/chrome/common/slide_animation.h b/chrome/common/slide_animation.h
index 42e9e65..f690e48 100644
--- a/chrome/common/slide_animation.h
+++ b/chrome/common/slide_animation.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_SLIDE_COMMON_ANIMATION_H__
-#define CHROME_SLIDE_COMMON_ANIMATION_H__
+#ifndef CHROME_COMMON_SLIDE_ANIMATION_H_
+#define CHROME_COMMON_SLIDE_ANIMATION_H_
#include "chrome/common/animation.h"
@@ -97,5 +97,4 @@ class SlideAnimation : public Animation {
int slide_duration_;
};
-#endif // CHROME_COMMON_SLIDE_ANIMATION_H__
-
+#endif // CHROME_COMMON_SLIDE_ANIMATION_H_
diff --git a/chrome/common/win_util.cc b/chrome/common/win_util.cc
index 243d629..aba3aad 100644
--- a/chrome/common/win_util.cc
+++ b/chrome/common/win_util.cc
@@ -617,7 +617,9 @@ void SetChildBounds(HWND child_window, HWND parent_window,
IsWindowVisible(insert_after_window))
window = insert_after_window;
- HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST);
+ POINT window_point = { bounds.x(), bounds.y() };
+ HMONITOR monitor = MonitorFromPoint(window_point,
+ MONITOR_DEFAULTTONEAREST);
if (monitor) {
MONITORINFO mi = {0};
mi.cbSize = sizeof(mi);