diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-15 04:24:41 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-15 04:24:41 +0000 |
commit | 4101aebea15c3b1411a017a19f1df97fd2a4ffcb (patch) | |
tree | 4cb81322bef4f8313f5edc4b833c7893b0279c8c /views | |
parent | a769d6e25f36e3b6fa1580d932117a7aa7021cdc (diff) | |
download | chromium_src-4101aebea15c3b1411a017a19f1df97fd2a4ffcb.zip chromium_src-4101aebea15c3b1411a017a19f1df97fd2a4ffcb.tar.gz chromium_src-4101aebea15c3b1411a017a19f1df97fd2a4ffcb.tar.bz2 |
Begin implementing a new widget.
This brings over code from ui/views, and bandaids it into compiling with the existing RootView/Widget interface. It compiles but probably doesn't run, which is OK since no one uses it.
BUG=72040
TEST=none
Review URL: http://codereview.chromium.org/6523008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@74915 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r-- | views/views.gyp | 11 | ||||
-rw-r--r-- | views/widget/native_widget.h | 10 | ||||
-rw-r--r-- | views/widget/native_widget_listener.h | 5 | ||||
-rw-r--r-- | views/widget/native_widget_win.cc | 681 | ||||
-rw-r--r-- | views/widget/native_widget_win.h | 274 | ||||
-rw-r--r-- | views/widget/native_widget_win_unittest.cc | 90 | ||||
-rw-r--r-- | views/widget/widget_impl.cc | 355 | ||||
-rw-r--r-- | views/widget/widget_impl.h | 167 | ||||
-rw-r--r-- | views/widget/widget_impl_test_util.cc | 34 | ||||
-rw-r--r-- | views/widget/widget_impl_test_util.h | 22 | ||||
-rw-r--r-- | views/widget/widget_impl_unittest.cc | 38 |
11 files changed, 1563 insertions, 124 deletions
diff --git a/views/views.gyp b/views/views.gyp index df8abce..d41d1b1 100644 --- a/views/views.gyp +++ b/views/views.gyp @@ -346,6 +346,12 @@ 'widget/tooltip_window_gtk.h', 'widget/monitor_win.cc', 'widget/monitor_win.h', + 'widget/native_widget.h', + 'widget/native_widget_listener.h', + 'widget/native_widget_win.cc', + 'widget/native_widget_win.h', + 'widget/widget_impl.cc', + 'widget/widget_impl.h', 'widget/widget.h', 'widget/widget_gtk.cc', 'widget/widget_gtk.h', @@ -411,9 +417,9 @@ 'widget/child_window_message_processor.h', 'widget/aero_tooltip_manager.cc', 'widget/root_view_drop_target.cc', + 'widget/widget_win.cc', 'window/hit_test.cc', 'window/native_frame_view.cc', - 'widget/widget_win.cc', ], }], ['touchui==1', { @@ -488,6 +494,9 @@ 'run_all_unittests.cc', 'test/test_views_delegate.h', 'view_unittest.cc', + 'widget/native_widget_win_unittest.cc', + 'widget/widget_impl_test_util.cc', + 'widget/widget_impl_test_util.h', 'widget/widget_win_unittest.cc', 'window/window_win_unittest.cc', diff --git a/views/widget/native_widget.h b/views/widget/native_widget.h index a173fc2..3b8572c 100644 --- a/views/widget/native_widget.h +++ b/views/widget/native_widget.h @@ -5,7 +5,7 @@ #ifndef VIEWS_WIDGET_NATIVE_WIDGET_H_ #define VIEWS_WIDGET_NATIVE_WIDGET_H_ -#include "ui/views/native_types.h" +#include "views/native_types.h" namespace gfx{ class Path; @@ -17,7 +17,7 @@ namespace internal { class NativeWidgetListener; } class View; -class Widget; +class WidgetImpl; //////////////////////////////////////////////////////////////////////////////// // NativeWidget interface @@ -44,7 +44,7 @@ class NativeWidget { // NativeView, or NULL if there is no NativeWidget that contains it. static NativeWidget* GetTopLevelNativeWidget(gfx::NativeView native_view); - // See Widget for documentation and notes. + // See WidgetImpl for documentation and notes. virtual void InitWithNativeViewParent(gfx::NativeView parent, const gfx::Rect& bounds) = 0; virtual void SetNativeWindowProperty(const char* name, void* value) = 0; @@ -69,9 +69,11 @@ class NativeWidget { virtual void InvalidateRect(const gfx::Rect& invalid_rect) = 0; virtual void Paint() = 0; virtual void FocusNativeView(gfx::NativeView native_view) = 0; - virtual Widget* GetWidget() const = 0; + virtual WidgetImpl* GetWidgetImpl() = 0; + virtual const WidgetImpl* GetWidgetImpl() const = 0; }; } // namespace views #endif // VIEWS_WIDGET_NATIVE_WIDGET_H_ + diff --git a/views/widget/native_widget_listener.h b/views/widget/native_widget_listener.h index e3e5de1..d9f524d 100644 --- a/views/widget/native_widget_listener.h +++ b/views/widget/native_widget_listener.h @@ -5,8 +5,6 @@ #ifndef VIEWS_WIDGET_NATIVE_WIDGET_LISTENER_H_ #define VIEWS_WIDGET_NATIVE_WIDGET_LISTENER_H_ -#include "ui/gfx/native_widget_types.h" - namespace gfx { class Canvas; class Point; @@ -53,7 +51,8 @@ class NativeWidgetListener { virtual void OnWorkAreaChanged() = 0; - virtual WidgetImpl* GetWidgetImpl() const = 0; + virtual WidgetImpl* GetWidgetImpl() = 0; + virtual const WidgetImpl* GetWidgetImpl() const = 0; }; } // namespace internal diff --git a/views/widget/native_widget_win.cc b/views/widget/native_widget_win.cc new file mode 100644 index 0000000..b75285e --- /dev/null +++ b/views/widget/native_widget_win.cc @@ -0,0 +1,681 @@ +// Copyright (c) 2011 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 "views/widget/native_widget_win.h" + +#include "base/scoped_ptr.h" +#include "ui/base/system_monitor/system_monitor.h" +#include "ui/base/view_prop.h" +#include "ui/base/win/hwnd_util.h" +#include "ui/gfx/canvas_skia.h" +#include "ui/gfx/native_theme_win.h" +#include "ui/gfx/path.h" +#include "views/view.h" +#include "views/widget/widget_impl.h" + +namespace views { +namespace internal { + +namespace { + +// Called from NativeWidgetWin::Paint() to asynchronously redraw child windows. +BOOL CALLBACK EnumChildProcForRedraw(HWND hwnd, LPARAM lparam) { + DWORD process_id; + GetWindowThreadProcessId(hwnd, &process_id); + gfx::Rect invalid_rect = *reinterpret_cast<gfx::Rect*>(lparam); + + RECT window_rect; + GetWindowRect(hwnd, &window_rect); + invalid_rect.Offset(-window_rect.left, -window_rect.top); + + int flags = RDW_INVALIDATE | RDW_NOCHILDREN | RDW_FRAME; + if (process_id == GetCurrentProcessId()) + flags |= RDW_UPDATENOW; + RedrawWindow(hwnd, &invalid_rect.ToRECT(), NULL, flags); + return TRUE; +} + +// Links the HWND to its Widget. +const char* const kNativeWidgetKey = "__VIEWS_NATIVE_WIDGET__"; + +// A custom MSAA object id used to determine if a screen reader is actively +// listening for MSAA events. +const int kMSAAObjectID = 1; + +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetWin, public: + +NativeWidgetWin::NativeWidgetWin(NativeWidgetListener* listener) + : listener_(listener), + active_mouse_tracking_flags_(0), + has_capture_(false) { +} + +NativeWidgetWin::~NativeWidgetWin() { +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetWin, NativeWidget implementation: + +void NativeWidgetWin::InitWithNativeViewParent(gfx::NativeView parent, + const gfx::Rect& bounds) { + WindowImpl::Init(parent, bounds); +} + +void NativeWidgetWin::SetNativeWindowProperty(const char* name, void* value) { + // Remove the existing property (if any). + for (ViewProps::iterator i = props_.begin(); i != props_.end(); ++i) { + if ((*i)->Key() == name) { + props_.erase(i); + break; + } + } + + if (value) + props_.push_back(new ui::ViewProp(hwnd(), name, value)); +} + +void* NativeWidgetWin::GetNativeWindowProperty(const char* name) const { + return ui::ViewProp::GetValue(hwnd(), name); +} + +gfx::Rect NativeWidgetWin::GetWindowScreenBounds() const { + RECT r; + GetWindowRect(hwnd(), &r); + return gfx::Rect(r); +} + +gfx::Rect NativeWidgetWin::GetClientAreaScreenBounds() const { + RECT r; + GetClientRect(hwnd(), &r); + POINT point = { r.left, r.top }; + ClientToScreen(hwnd(), &point); + return gfx::Rect(point.x, point.y, r.right - r.left, r.bottom - r.top); +} + +void NativeWidgetWin::SetBounds(const gfx::Rect& bounds) { + SetWindowPos(hwnd(), NULL, bounds.x(), bounds.y(), bounds.width(), + bounds.height(), SWP_NOACTIVATE | SWP_NOZORDER); +} + +void NativeWidgetWin::SetShape(const gfx::Path& shape) { + SetWindowRgn(hwnd(), shape.CreateNativeRegion(), TRUE); +} + +gfx::NativeView NativeWidgetWin::GetNativeView() const { + return hwnd(); +} + +void NativeWidgetWin::Show() { + if (IsWindow(hwnd())) + ShowWindow(hwnd(), SW_SHOWNOACTIVATE); + // TODO(beng): move to windowposchanging to trap visibility changes instead. + if (IsLayeredWindow()) + Invalidate(); +} + +void NativeWidgetWin::Hide() { + if (IsWindow(hwnd())) { + // NOTE: Be careful not to activate any windows here (for example, calling + // ShowWindow(SW_HIDE) will automatically activate another window). This + // code can be called while a window is being deactivated, and activating + // another window will screw up the activation that is already in progress. + SetWindowPos(hwnd(), NULL, 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); + } +} + +void NativeWidgetWin::Close() { + DestroyWindow(hwnd()); +} + +void NativeWidgetWin::MoveAbove(NativeWidget* other) { + SetWindowPos(hwnd(), other->GetNativeView(), 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); +} + +void NativeWidgetWin::SetAlwaysOnTop(bool always_on_top) { + DWORD style = always_on_top ? window_ex_style() | WS_EX_TOPMOST + : window_ex_style() & ~WS_EX_TOPMOST; + set_window_ex_style(style); + SetWindowLong(hwnd(), GWL_EXSTYLE, window_ex_style()); +} + +bool NativeWidgetWin::IsVisible() const { + return !!IsWindowVisible(hwnd()); +} + +bool NativeWidgetWin::IsActive() const { + WINDOWINFO info; + return ::GetWindowInfo(hwnd(), &info) && + ((info.dwWindowStatus & WS_ACTIVECAPTION) != 0); +} + +void NativeWidgetWin::SetMouseCapture() { + SetCapture(hwnd()); + has_capture_ = true; +} + +void NativeWidgetWin::ReleaseMouseCapture() { + ReleaseCapture(); + has_capture_ = false; +} + +bool NativeWidgetWin::HasMouseCapture() const { + return has_capture_; +} + +bool NativeWidgetWin::ShouldReleaseCaptureOnMouseReleased() const { + return true; +} + +void NativeWidgetWin::Invalidate() { + ::InvalidateRect(hwnd(), NULL, FALSE); +} + +void NativeWidgetWin::InvalidateRect(const gfx::Rect& invalid_rect) { + // InvalidateRect() expects client coordinates. + RECT r = invalid_rect.ToRECT(); + ::InvalidateRect(hwnd(), &r, FALSE); +} + +void NativeWidgetWin::Paint() { + RECT r; + GetUpdateRect(hwnd(), &r, FALSE); + if (!IsRectEmpty(&r)) { + // TODO(beng): WS_EX_TRANSPARENT windows (see WidgetWin::opaque_) + // Paint child windows that are in a different process asynchronously. + // This prevents a hang in other processes from blocking this process. + + // Calculate the invalid rect in screen coordinates before the first + // RedrawWindow() call to the parent HWND, since that will empty update_rect + // (which comes from a member variable) in the OnPaint call. + gfx::Rect screen_rect = GetWindowScreenBounds(); + gfx::Rect invalid_screen_rect(r); + invalid_screen_rect.Offset(screen_rect.x(), screen_rect.y()); + + RedrawWindow(hwnd(), &r, NULL, + RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN); + + LPARAM lparam = reinterpret_cast<LPARAM>(&invalid_screen_rect); + EnumChildWindows(hwnd(), EnumChildProcForRedraw, lparam); + } +} + +void NativeWidgetWin::FocusNativeView(gfx::NativeView native_view) { + if (IsWindow(native_view)) { + if (GetFocus() != native_view) + SetFocus(native_view); + } else { + // NULL or invalid |native_view| passed, we consider this to be clearing + // focus. Keep the top level window focused so we continue to receive + // key events. + SetFocus(hwnd()); + } +} + +WidgetImpl* NativeWidgetWin::GetWidgetImpl() { + return listener_->GetWidgetImpl(); +} + +const WidgetImpl* NativeWidgetWin::GetWidgetImpl() const { + return listener_->GetWidgetImpl(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidetWin, MessageLoopForUI::Observer implementation + +void NativeWidgetWin::WillProcessMessage(const MSG& msg) { +} + +void NativeWidgetWin::DidProcessMessage(const MSG& msg) { + // We need to add ourselves as a message loop observer so that we can repaint + // aggressively if the contents of our window become invalid. Unfortunately + // WM_PAINT messages are starved and we get flickery redrawing when resizing + // if we do not do this. + Paint(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetWin, message handlers: + +void NativeWidgetWin::OnActivate(UINT action, BOOL minimized, HWND window) { + SetMsgHandled(FALSE); +} + +void NativeWidgetWin::OnActivateApp(BOOL active, DWORD thread_id) { + SetMsgHandled(FALSE); +} + +LRESULT NativeWidgetWin::OnAppCommand(HWND window, short app_command, + WORD device, int keystate) { + SetMsgHandled(FALSE); + return 0; +} + +void NativeWidgetWin::OnCancelMode() { +} + +void NativeWidgetWin::OnCaptureChanged(HWND hwnd) { + has_capture_ = false; + listener_->OnMouseCaptureLost(); +} + +void NativeWidgetWin::OnClose() { + listener_->OnClose(); +} + +void NativeWidgetWin::OnCommand(UINT notification_code, int command_id, + HWND window) { + SetMsgHandled(FALSE); +} + +LRESULT NativeWidgetWin::OnCreate(CREATESTRUCT* create_struct) { + SetNativeWindowProperty(kNativeWidgetKey, this); + listener_->OnNativeWidgetCreated(); + MessageLoopForUI::current()->AddObserver(this); + return 0; +} + +void NativeWidgetWin::OnDestroy() { + // TODO(beng): drop_target_ + props_.reset(); +} + +void NativeWidgetWin::OnDisplayChange(UINT bits_per_pixel, CSize screen_size) { + listener_->OnDisplayChanged(); +} + +LRESULT NativeWidgetWin::OnDwmCompositionChanged(UINT message, + WPARAM w_param, + LPARAM l_param) { + SetMsgHandled(FALSE); + return 0; +} + +void NativeWidgetWin::OnEndSession(BOOL ending, UINT logoff) { + SetMsgHandled(FALSE); +} + +void NativeWidgetWin::OnEnterSizeMove() { + SetMsgHandled(FALSE); +} + +LRESULT NativeWidgetWin::OnEraseBkgnd(HDC dc) { + // This is needed for magical win32 flicker ju-ju + return 1; +} + +void NativeWidgetWin::OnExitMenuLoop(BOOL is_track_popup_menu) { + SetMsgHandled(FALSE); +} + +void NativeWidgetWin::OnExitSizeMove() { + SetMsgHandled(FALSE); +} + +LRESULT NativeWidgetWin::OnGetObject(UINT message, WPARAM w_param, + LPARAM l_param) { + return static_cast<LRESULT>(0L); +} + +void NativeWidgetWin::OnGetMinMaxInfo(MINMAXINFO* minmax_info) { + SetMsgHandled(FALSE); +} + +void NativeWidgetWin::OnHScroll(int scroll_type, short position, + HWND scrollbar) { + SetMsgHandled(FALSE); +} + +void NativeWidgetWin::OnInitMenu(HMENU menu) { + SetMsgHandled(FALSE); +} + +void NativeWidgetWin::OnInitMenuPopup(HMENU menu, UINT position, + BOOL is_system_menu) { + SetMsgHandled(FALSE); +} + +LRESULT NativeWidgetWin::OnKeyDown(UINT message, WPARAM w_param, + LPARAM l_param) { + MSG msg; + MakeMSG(&msg, message, w_param, l_param); + SetMsgHandled(listener_->OnKeyEvent(KeyEvent(msg))); + return 0; +} + +LRESULT NativeWidgetWin::OnKeyUp(UINT message, WPARAM w_param, LPARAM l_param) { + MSG msg; + MakeMSG(&msg, message, w_param, l_param); + SetMsgHandled(listener_->OnKeyEvent(KeyEvent(msg))); + return 0; +} + +void NativeWidgetWin::OnKillFocus(HWND focused_window) { + listener_->OnNativeBlur(focused_window); + SetMsgHandled(FALSE); +} + +LRESULT NativeWidgetWin::OnMouseActivate(HWND window, UINT hittest_code, + UINT message) { + SetMsgHandled(FALSE); + return MA_ACTIVATE; +} + +LRESULT NativeWidgetWin::OnMouseLeave(UINT message, WPARAM w_param, + LPARAM l_param) { + // TODO(beng): tooltip + MSG msg; + MakeMSG(&msg, message, w_param, l_param); + //SetMsgHandled(listener_->OnMouseEvent(MouseEvent(msg))); + + // Reset our tracking flag so that future mouse movement over this WidgetWin + // results in a new tracking session. + active_mouse_tracking_flags_ = 0; + + return 0; +} + +void NativeWidgetWin::OnMove(const CPoint& point) { + SetMsgHandled(FALSE); +} + +void NativeWidgetWin::OnMoving(UINT param, LPRECT new_bounds) { +} + +LRESULT NativeWidgetWin::OnMouseRange(UINT message, WPARAM w_param, + LPARAM l_param) { + // TODO(beng): tooltips + ProcessMouseRange(message, w_param, l_param, false); + return 0; +} + +LRESULT NativeWidgetWin::OnNCActivate(BOOL active) { + SetMsgHandled(FALSE); + return 0; +} + +LRESULT NativeWidgetWin::OnNCCalcSize(BOOL w_param, LPARAM l_param) { + SetMsgHandled(FALSE); + return 0; +} + +LRESULT NativeWidgetWin::OnNCHitTest(UINT message, WPARAM w_param, + LPARAM l_param) { + LRESULT lr = DefWindowProc(hwnd(), message, w_param, l_param); + return lr; +} + +LRESULT NativeWidgetWin::OnNCMouseRange(UINT message, WPARAM w_param, + LPARAM l_param) { + bool processed = ProcessMouseRange(message, w_param, l_param, true); + SetMsgHandled(FALSE); + return 0; +} + +void NativeWidgetWin::OnNCPaint(HRGN rgn) { + SetMsgHandled(FALSE); +} + +LRESULT NativeWidgetWin::OnNCUAHDrawCaption(UINT message, + WPARAM w_param, + LPARAM l_param) { + SetMsgHandled(FALSE); + return 0; +} + +LRESULT NativeWidgetWin::OnNCUAHDrawFrame(UINT message, + WPARAM w_param, + LPARAM l_param) { + SetMsgHandled(FALSE); + return 0; +} + +LRESULT NativeWidgetWin::OnNotify(int w_param, NMHDR* l_param) { + // TODO(beng): tooltips + SetMsgHandled(FALSE); + return 0; +} + +void NativeWidgetWin::OnPaint(HDC dc) { + if (IsLayeredWindow()) { + // We need to clip to the dirty rect ourselves. + window_contents_->save(SkCanvas::kClip_SaveFlag); + RECT r; + GetUpdateRect(hwnd(), &r, FALSE); + window_contents_->ClipRectInt(r.left, r.top, r.right - r.left, + r.bottom - r.top); + listener_->OnPaint(window_contents_.get()); + window_contents_->restore(); + + RECT wr; + GetWindowRect(hwnd(), &wr); + SIZE size = {wr.right - wr.left, wr.bottom - wr.top}; + POINT position = {wr.left, wr.top}; + HDC dib_dc = window_contents_->getTopPlatformDevice().getBitmapDC(); + POINT zero = {0, 0}; + BLENDFUNCTION blend = {AC_SRC_OVER, 0, 125, AC_SRC_ALPHA}; + UpdateLayeredWindow(hwnd(), NULL, &position, &size, dib_dc, &zero, + RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA); + } else { + scoped_ptr<gfx::CanvasPaint> canvas( + gfx::CanvasPaint::CreateCanvasPaint(hwnd())); + listener_->OnPaint(canvas->AsCanvas()); + } +} + +LRESULT NativeWidgetWin::OnPowerBroadcast(DWORD power_event, DWORD data) { + ui::SystemMonitor* monitor = ui::SystemMonitor::Get(); + if (monitor) + monitor->ProcessWmPowerBroadcastMessage(power_event); + SetMsgHandled(FALSE); + return 0; +} + +LRESULT NativeWidgetWin::OnReflectedMessage(UINT message, WPARAM w_param, + LPARAM l_param) { + SetMsgHandled(FALSE); + return 0; +} + +void NativeWidgetWin::OnSetFocus(HWND focused_window) { + listener_->OnNativeFocus(focused_window); + SetMsgHandled(FALSE); +} + +LRESULT NativeWidgetWin::OnSetIcon(UINT size_type, HICON new_icon) { + SetMsgHandled(FALSE); + return 0; +} + +LRESULT NativeWidgetWin::OnSetText(const wchar_t* text) { + SetMsgHandled(FALSE); + return 0; +} + +void NativeWidgetWin::OnSettingChange(UINT flags, const wchar_t* section) { + if (flags == SPI_SETWORKAREA) + listener_->OnWorkAreaChanged(); + SetMsgHandled(FALSE); +} + +void NativeWidgetWin::OnSize(UINT param, const CSize& size) { + gfx::Size s(size.cx, size.cy); + listener_->OnSizeChanged(s); + if (IsLayeredWindow()) { + window_contents_.reset( + new gfx::CanvasSkia(s.width(), s.height(), false)); + } +} + +void NativeWidgetWin::OnSysCommand(UINT notification_code, CPoint click) { + SetMsgHandled(FALSE); +} + +void NativeWidgetWin::OnThemeChanged() { + gfx::NativeTheme::instance()->CloseHandles(); +} + +void NativeWidgetWin::OnVScroll(int scroll_type, short position, + HWND scrollbar) { + SetMsgHandled(FALSE); +} + +void NativeWidgetWin::OnWindowPosChanging(WINDOWPOS* window_pos) { + SetMsgHandled(FALSE); +} + +void NativeWidgetWin::OnWindowPosChanged(WINDOWPOS* window_pos) { + SetMsgHandled(FALSE); +} + +void NativeWidgetWin::OnFinalMessage(HWND window) { + delete this; +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetWin, WindowImpl overrides: + +HICON NativeWidgetWin::GetDefaultWindowIcon() const { + return NULL; +} + +LRESULT NativeWidgetWin::OnWndProc(UINT message, WPARAM w_param, + LPARAM l_param) { + LRESULT result = 0; + + // Otherwise we handle everything else. + if (!ProcessWindowMessage(hwnd(), message, w_param, l_param, result)) + result = DefWindowProc(hwnd(), message, w_param, l_param); + if (message == WM_NCDESTROY) { + MessageLoopForUI::current()->RemoveObserver(this); + OnFinalMessage(hwnd()); + } + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetWin, private: + +void NativeWidgetWin::TrackMouseEvents(DWORD mouse_tracking_flags) { + // Begin tracking mouse events for this HWND so that we get WM_MOUSELEAVE + // when the user moves the mouse outside this HWND's bounds. + if (active_mouse_tracking_flags_ == 0 || mouse_tracking_flags & TME_CANCEL) { + if (mouse_tracking_flags & TME_CANCEL) { + // We're about to cancel active mouse tracking, so empty out the stored + // state. + active_mouse_tracking_flags_ = 0; + } else { + active_mouse_tracking_flags_ = mouse_tracking_flags; + } + + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(tme); + tme.dwFlags = mouse_tracking_flags; + tme.hwndTrack = hwnd(); + tme.dwHoverTime = 0; + TrackMouseEvent(&tme); + } else if (mouse_tracking_flags != active_mouse_tracking_flags_) { + TrackMouseEvents(active_mouse_tracking_flags_ | TME_CANCEL); + TrackMouseEvents(mouse_tracking_flags); + } +} + +bool NativeWidgetWin::ProcessMouseRange(UINT message, WPARAM w_param, + LPARAM l_param, bool non_client) { + MSG msg; + MakeMSG(&msg, message, w_param, l_param); + if (message == WM_MOUSEWHEEL) { + // Reroute the mouse-wheel to the window under the mouse pointer if + // applicable. + // TODO(beng): + //if (views::RerouteMouseWheel(hwnd(), w_param, l_param)) + // return 0; + //return listener_->OnMouseWheelEvent(MouseWheelEvent(msg)); + return 0; + } + // Windows only fires WM_MOUSELEAVE events if the application begins + // "tracking" mouse events for a given HWND during WM_MOUSEMOVE events. + // We need to call |TrackMouseEvents| to listen for WM_MOUSELEAVE. + if (!has_capture_) + TrackMouseEvents(non_client ? TME_NONCLIENT | TME_LEAVE : TME_LEAVE); + //return listener_->OnMouseEvent(MouseEvent(msg)); + return 0; +} + +void NativeWidgetWin::MakeMSG(MSG* msg, UINT message, WPARAM w_param, + LPARAM l_param) const { + msg->hwnd = hwnd(); + msg->message = message; + msg->wParam = w_param; + msg->lParam = l_param; + msg->time = 0; + msg->pt.x = msg->pt.y = 0; +} + +void NativeWidgetWin::CloseNow() { + DestroyWindow(hwnd()); +} + +bool NativeWidgetWin::IsLayeredWindow() const { + return !!(window_ex_style() & WS_EX_LAYERED); +} + +} // namespace internal + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidget, public: + +// static +NativeWidget* NativeWidget::CreateNativeWidget( + internal::NativeWidgetListener* listener) { + return new internal::NativeWidgetWin(listener); +} + +// static +NativeWidget* NativeWidget::GetNativeWidgetForNativeView( + gfx::NativeView native_view) { + if (!ui::WindowImpl::IsWindowImpl(native_view)) + return NULL; + return reinterpret_cast<internal::NativeWidgetWin*>( + ui::ViewProp::GetValue(native_view, internal::kNativeWidgetKey)); +} + +// static +NativeWidget* NativeWidget::GetNativeWidgetForNativeWindow( + gfx::NativeWindow native_window) { + return GetNativeWidgetForNativeView(native_window); +} + +// static +NativeWidget* NativeWidget::GetTopLevelNativeWidget( + gfx::NativeView native_view) { + // First, check if the top-level window is a Widget. + HWND root = ::GetAncestor(native_view, GA_ROOT); + if (!root) + return NULL; + + NativeWidget* widget = GetNativeWidgetForNativeView(root); + if (widget) + return widget; + + // Second, try to locate the last Widget window in the parent hierarchy. + HWND parent_hwnd = native_view; + NativeWidget* parent_widget; + do { + parent_widget = GetNativeWidgetForNativeView(parent_hwnd); + if (parent_widget) { + widget = parent_widget; + parent_hwnd = ::GetAncestor(parent_hwnd, GA_PARENT); + } + } while (parent_hwnd != NULL && parent_widget != NULL); + + return widget; +} + +} // namespace views diff --git a/views/widget/native_widget_win.h b/views/widget/native_widget_win.h new file mode 100644 index 0000000..dc3f239 --- /dev/null +++ b/views/widget/native_widget_win.h @@ -0,0 +1,274 @@ +// Copyright (c) 2011 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 VIEWS_WIDGET_NATIVE_WIDGET_WIN_H_ +#define VIEWS_WIDGET_NATIVE_WIDGET_WIN_H_ + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "base/scoped_vector.h" +#include "ui/base/win/window_impl.h" +#include "views/widget/native_widget.h" + +namespace gfx { +class CanvasSkia; +} + +namespace ui { +class ViewProp; +} + +namespace views { +class WidgetImpl; + +namespace internal { + +// A Windows message reflected from other windows. This message is sent with the +// following arguments: +// HWND - Target window +// MSG - kReflectedMessage +// WPARAM - Should be 0 +// LPARAM - Pointer to MSG struct containing the original message. +const int kReflectedMessage = WM_APP + 3; + +// These two messages aren't defined in winuser.h, but they are sent to windows +// with captions. They appear to paint the window caption and frame. +// Unfortunately if you override the standard non-client rendering as we do +// with CustomFrameWindow, sometimes Windows (not deterministically +// reproducibly but definitely frequently) will send these messages to the +// window and paint the standard caption/title over the top of the custom one. +// So we need to handle these messages in CustomFrameWindow to prevent this +// from happening. +const int WM_NCUAHDRAWCAPTION = 0xAE; +const int WM_NCUAHDRAWFRAME = 0xAF; + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetWin class +// +// A NativeWidget implementation that wraps a Win32 HWND. +// +class NativeWidgetWin : public NativeWidget, + public ui::WindowImpl, + public MessageLoopForUI::Observer { + public: + explicit NativeWidgetWin(NativeWidgetListener* listener); + virtual ~NativeWidgetWin(); + + private: + typedef ScopedVector<ui::ViewProp> ViewProps; + + // Overridden from NativeWidget: + virtual void InitWithNativeViewParent(gfx::NativeView parent, + const gfx::Rect& bounds); + virtual void SetNativeWindowProperty(const char* name, void* value); + virtual void* GetNativeWindowProperty(const char* name) const; + virtual gfx::Rect GetWindowScreenBounds() const; + virtual gfx::Rect GetClientAreaScreenBounds() const; + virtual void SetBounds(const gfx::Rect& bounds); + virtual void SetShape(const gfx::Path& shape); + virtual gfx::NativeView GetNativeView() const; + virtual void Show(); + virtual void Hide(); + virtual void Close(); + virtual void MoveAbove(NativeWidget* other); + virtual void SetAlwaysOnTop(bool always_on_top); + virtual bool IsVisible() const; + virtual bool IsActive() const; + virtual void SetMouseCapture(); + virtual void ReleaseMouseCapture(); + virtual bool HasMouseCapture() const; + virtual bool ShouldReleaseCaptureOnMouseReleased() const; + virtual void Invalidate(); + virtual void InvalidateRect(const gfx::Rect& invalid_rect); + virtual void Paint(); + virtual void FocusNativeView(gfx::NativeView native_view); + virtual WidgetImpl* GetWidgetImpl(); + virtual const WidgetImpl* GetWidgetImpl() const; + + // Overridden from MessageLoop::Observer: + void WillProcessMessage(const MSG& msg); + virtual void DidProcessMessage(const MSG& msg); + + // Message handlers + BEGIN_MSG_MAP_EX(NativeWidgetWin) + // Range handlers must go first! + MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange) + MESSAGE_RANGE_HANDLER_EX(WM_NCMOUSEMOVE, WM_NCMBUTTONDBLCLK, OnNCMouseRange) + + // Reflected message handler + MESSAGE_HANDLER_EX(kReflectedMessage, OnReflectedMessage) + + // CustomFrameWindow hacks + MESSAGE_HANDLER_EX(WM_NCUAHDRAWCAPTION, OnNCUAHDrawCaption) + MESSAGE_HANDLER_EX(WM_NCUAHDRAWFRAME, OnNCUAHDrawFrame) + + // Vista and newer + MESSAGE_HANDLER_EX(WM_DWMCOMPOSITIONCHANGED, OnDwmCompositionChanged) + + // Non-atlcrack.h handlers + MESSAGE_HANDLER_EX(WM_GETOBJECT, OnGetObject) + MESSAGE_HANDLER_EX(WM_NCMOUSELEAVE, OnMouseLeave) + MESSAGE_HANDLER_EX(WM_MOUSELEAVE, OnMouseLeave) + + // Key events. + MESSAGE_HANDLER_EX(WM_KEYDOWN, OnKeyDown) + MESSAGE_HANDLER_EX(WM_KEYUP, OnKeyUp) + MESSAGE_HANDLER_EX(WM_SYSKEYDOWN, OnKeyDown); + MESSAGE_HANDLER_EX(WM_SYSKEYUP, OnKeyUp); + + // This list is in _ALPHABETICAL_ order! OR I WILL HURT YOU. + MSG_WM_ACTIVATE(OnActivate) + MSG_WM_ACTIVATEAPP(OnActivateApp) + MSG_WM_APPCOMMAND(OnAppCommand) + MSG_WM_CANCELMODE(OnCancelMode) + MSG_WM_CAPTURECHANGED(OnCaptureChanged) + MSG_WM_CLOSE(OnClose) + MSG_WM_COMMAND(OnCommand) + MSG_WM_CREATE(OnCreate) + MSG_WM_DESTROY(OnDestroy) + MSG_WM_DISPLAYCHANGE(OnDisplayChange) + MSG_WM_ERASEBKGND(OnEraseBkgnd) + MSG_WM_ENDSESSION(OnEndSession) + MSG_WM_ENTERSIZEMOVE(OnEnterSizeMove) + MSG_WM_EXITMENULOOP(OnExitMenuLoop) + MSG_WM_EXITSIZEMOVE(OnExitSizeMove) + MSG_WM_GETMINMAXINFO(OnGetMinMaxInfo) + MSG_WM_HSCROLL(OnHScroll) + MSG_WM_INITMENU(OnInitMenu) + MSG_WM_INITMENUPOPUP(OnInitMenuPopup) + MSG_WM_KILLFOCUS(OnKillFocus) + MSG_WM_MOUSEACTIVATE(OnMouseActivate) + MSG_WM_MOVE(OnMove) + MSG_WM_MOVING(OnMoving) + MSG_WM_NCACTIVATE(OnNCActivate) + MSG_WM_NCCALCSIZE(OnNCCalcSize) + MESSAGE_HANDLER_EX(WM_NCHITTEST, OnNCHitTest) + MSG_WM_NCPAINT(OnNCPaint) + MSG_WM_NOTIFY(OnNotify) + MSG_WM_PAINT(OnPaint) + MSG_WM_POWERBROADCAST(OnPowerBroadcast) + MSG_WM_SETFOCUS(OnSetFocus) + MSG_WM_SETICON(OnSetIcon) + MSG_WM_SETTEXT(OnSetText) + MSG_WM_SETTINGCHANGE(OnSettingChange) + MSG_WM_SIZE(OnSize) + MSG_WM_SYSCOMMAND(OnSysCommand) + MSG_WM_THEMECHANGED(OnThemeChanged) + MSG_WM_VSCROLL(OnVScroll) + MSG_WM_WINDOWPOSCHANGING(OnWindowPosChanging) + MSG_WM_WINDOWPOSCHANGED(OnWindowPosChanged) + END_MSG_MAP() + + virtual void OnActivate(UINT action, BOOL minimized, HWND window); + virtual void OnActivateApp(BOOL active, DWORD thread_id); + virtual LRESULT OnAppCommand(HWND window, short app_command, WORD device, + int keystate); + virtual void OnCancelMode(); + virtual void OnCaptureChanged(HWND hwnd); + virtual void OnClose(); + virtual void OnCommand(UINT notification_code, int command_id, HWND window); + virtual LRESULT OnCreate(CREATESTRUCT* create_struct); + // WARNING: If you override this be sure and invoke super, otherwise we'll + // leak a few things. + virtual void OnDestroy(); + virtual void OnDisplayChange(UINT bits_per_pixel, CSize screen_size); + virtual LRESULT OnDwmCompositionChanged(UINT message, + WPARAM w_param, + LPARAM l_param); + virtual void OnEndSession(BOOL ending, UINT logoff); + virtual void OnEnterSizeMove(); + virtual LRESULT OnEraseBkgnd(HDC dc); + virtual void OnExitMenuLoop(BOOL is_track_popup_menu); + virtual void OnExitSizeMove(); + virtual LRESULT OnGetObject(UINT message, WPARAM w_param, LPARAM l_param); + virtual void OnGetMinMaxInfo(MINMAXINFO* minmax_info); + virtual void OnHScroll(int scroll_type, short position, HWND scrollbar); + virtual void OnInitMenu(HMENU menu); + virtual void OnInitMenuPopup(HMENU menu, UINT position, BOOL is_system_menu); + virtual LRESULT OnKeyDown(UINT message, WPARAM w_param, LPARAM l_param); + virtual LRESULT OnKeyUp(UINT message, WPARAM w_param, LPARAM l_param); + virtual void OnKillFocus(HWND focused_window); + virtual LRESULT OnMouseActivate(HWND window, UINT hittest_code, UINT message); + virtual LRESULT OnMouseLeave(UINT message, WPARAM w_param, LPARAM l_param); + virtual void OnMove(const CPoint& point); + virtual void OnMoving(UINT param, LPRECT new_bounds); + virtual LRESULT OnMouseRange(UINT message, WPARAM w_param, LPARAM l_param); + virtual LRESULT OnNCActivate(BOOL active); + virtual LRESULT OnNCCalcSize(BOOL w_param, LPARAM l_param); + virtual LRESULT OnNCHitTest(UINT message, WPARAM w_param, LPARAM l_param); + virtual LRESULT OnNCMouseRange(UINT message, WPARAM w_param, LPARAM l_param); + virtual void OnNCPaint(HRGN rgn); + virtual LRESULT OnNCUAHDrawCaption(UINT message, + WPARAM w_param, + LPARAM l_param); + virtual LRESULT OnNCUAHDrawFrame(UINT message, WPARAM w_param, + LPARAM l_param); + virtual LRESULT OnNotify(int w_param, NMHDR* l_param); + virtual void OnPaint(HDC dc); + virtual LRESULT OnPowerBroadcast(DWORD power_event, DWORD data); + virtual LRESULT OnReflectedMessage(UINT message, WPARAM w_param, + LPARAM l_param); + virtual void OnSetFocus(HWND focused_window); + virtual LRESULT OnSetIcon(UINT size_type, HICON new_icon); + virtual LRESULT OnSetText(const wchar_t* text); + virtual void OnSettingChange(UINT flags, const wchar_t* section); + virtual void OnSize(UINT param, const CSize& size); + virtual void OnSysCommand(UINT notification_code, CPoint click); + virtual void OnThemeChanged(); + virtual void OnVScroll(int scroll_type, short position, HWND scrollbar); + virtual void OnWindowPosChanging(WINDOWPOS* window_pos); + virtual void OnWindowPosChanged(WINDOWPOS* window_pos); + + // Deletes this window as it is destroyed, override to provide different + // behavior. + virtual void OnFinalMessage(HWND window); + + // Overridden from WindowImpl: + virtual HICON GetDefaultWindowIcon() const; + virtual LRESULT OnWndProc(UINT message, WPARAM w_param, LPARAM l_param); + + // Start tracking all mouse events so that this window gets sent mouse leave + // messages too. + void TrackMouseEvents(DWORD mouse_tracking_flags); + + bool ProcessMouseRange(UINT message, WPARAM w_param, LPARAM l_param, + bool non_client); + void ProcessMouseMoved(const CPoint& point, UINT flags, bool is_nonclient); + void ProcessMouseExited(); + + // Fills out a MSG struct with the supplied values. + void MakeMSG(MSG* msg, UINT message, WPARAM w_param, LPARAM l_param) const; + + void CloseNow(); + + bool IsLayeredWindow() const; + + // A listener implementation that handles events received here. + NativeWidgetListener* listener_; + + // The flags currently being used with TrackMouseEvent to track mouse + // messages. 0 if there is no active tracking. The value of this member is + // used when tracking is canceled. + DWORD active_mouse_tracking_flags_; + + // True when the HWND has event capture. + bool has_capture_; + + // A canvas that contains the window contents in the case of a layered + // window. + scoped_ptr<gfx::CanvasSkia> window_contents_; + + // Properties associated with this NativeWidget implementation. + // TODO(beng): move to WidgetImpl. + ViewProps props_; + + DISALLOW_COPY_AND_ASSIGN(NativeWidgetWin); +}; + +} // namespace internal +} // namespace views + +#endif // VIEWS_WIDGET_NATIVE_WIDGET_WIN_H_ + diff --git a/views/widget/native_widget_win_unittest.cc b/views/widget/native_widget_win_unittest.cc new file mode 100644 index 0000000..b29d360 --- /dev/null +++ b/views/widget/native_widget_win_unittest.cc @@ -0,0 +1,90 @@ +// Copyright (c) 2011 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 "testing/gtest/include/gtest/gtest.h" +#include "ui/base/win/window_impl.h" +#include "views/view.h" +#include "views/widget/native_widget.h" +#include "views/widget/widget_impl.h" +#include "views/widget/widget_impl_test_util.h" + +namespace views { + +class NativeWidgetTest : public testing::Test { + public: + NativeWidgetTest() {} + virtual ~NativeWidgetTest() {} + + private: + DISALLOW_COPY_AND_ASSIGN(NativeWidgetTest); +}; + +class TestWindowImpl : public ui::WindowImpl { + public: + TestWindowImpl() {} + virtual ~TestWindowImpl() {} + + virtual BOOL ProcessWindowMessage(HWND window, + UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT& result, + DWORD msg_mad_id = 0) { + return FALSE; + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestWindowImpl); +}; + +#if 0 + +TEST_F(NativeWidgetTest, CreateNativeWidget) { + scoped_ptr<WidgetImpl> widget(internal::CreateWidgetImpl()); + EXPECT_TRUE(widget->native_widget()->GetNativeView() != NULL); +} + +TEST_F(NativeWidgetTest, GetNativeWidgetForNativeView) { + scoped_ptr<WidgetImpl> widget(internal::CreateWidgetImpl()); + NativeWidget* a = widget->native_widget(); + HWND nv = widget->native_widget()->GetNativeView(); + NativeWidget* b = NativeWidget::GetNativeWidgetForNativeView(nv); + EXPECT_EQ(a, b); +} + +// |widget| has the toplevel NativeWidget. +TEST_F(NativeWidgetTest, GetTopLevelNativeWidget1) { + scoped_ptr<WidgetImpl> widget(internal::CreateWidgetImpl()); + EXPECT_EQ(widget->native_widget(), + NativeWidget::GetTopLevelNativeWidget( + widget->native_widget()->GetNativeView())); +} + +// |toplevel_widget| has the toplevel NativeWidget. +TEST_F(NativeWidgetTest, GetTopLevelNativeWidget2) { + scoped_ptr<WidgetImpl> child_widget(internal::CreateWidgetImpl()); + scoped_ptr<WidgetImpl> toplevel_widget(internal::CreateWidgetImpl()); + SetParent(child_widget->native_widget()->GetNativeView(), + toplevel_widget->native_widget()->GetNativeView()); + EXPECT_EQ(toplevel_widget->native_widget(), + NativeWidget::GetTopLevelNativeWidget( + child_widget->native_widget()->GetNativeView())); +} + +// |child_widget| has the toplevel NativeWidget. +TEST_F(NativeWidgetTest, GetTopLevelNativeWidget3) { + scoped_ptr<WidgetImpl> child_widget(internal::CreateWidgetImpl()); + + TestWindowImpl toplevel; + toplevel.Init(NULL, gfx::Rect(10, 10, 100, 100)); + + SetParent(child_widget->native_widget()->GetNativeView(), toplevel.hwnd()); + EXPECT_EQ(child_widget->native_widget(), + NativeWidget::GetTopLevelNativeWidget( + child_widget->native_widget()->GetNativeView())); +} + +#endif + +} // namespace views diff --git a/views/widget/widget_impl.cc b/views/widget/widget_impl.cc index c3292c9..0063a71 100644 --- a/views/widget/widget_impl.cc +++ b/views/widget/widget_impl.cc @@ -4,217 +4,412 @@ #include "views/widget/widget_impl.h" +#include "base/compiler_specific.h" +#include "base/message_loop.h" +#include "views/focus/focus_manager.h" +#include "views/view.h" +#include "views/widget/native_widget.h" +#include "views/widget/root_view.h" + namespace views { -//////////////////////////////////////////////////////////////////////////////// -// WidgetImpl, public: +namespace { -WidgetImpl::WidgetImpl() { +// TODO(beng): move to platform file +int GetHorizontalDragThreshold() { + static int threshold = -1; +#if defined(OS_WIN) + if (threshold == -1) + threshold = GetSystemMetrics(SM_CXDRAG) / 2; +#endif + return threshold; } -WidgetImpl::~WidgetImpl() { +// TODO(beng): move to platform file +int GetVerticalDragThreshold() { + static int threshold = -1; +#if defined(OS_WIN) + if (threshold == -1) + threshold = GetSystemMetrics(SM_CYDRAG) / 2; +#endif + return threshold; } -//////////////////////////////////////////////////////////////////////////////// -// WidgetImpl, Widget implementation: - -void WidgetImpl::Init(gfx::NativeView parent, const gfx::Rect& bounds) { +bool ExceededDragThreshold(int delta_x, int delta_y) { + return (abs(delta_x) > GetHorizontalDragThreshold() || + abs(delta_y) > GetVerticalDragThreshold()); +} } -void WidgetImpl::InitWithWidget(Widget* parent, const gfx::Rect& bounds) { +//////////////////////////////////////////////////////////////////////////////// +// WidgetImpl, public: +WidgetImpl::WidgetImpl(View* contents_view) + : ALLOW_THIS_IN_INITIALIZER_LIST( + native_widget_(NativeWidget::CreateNativeWidget(this))), + ALLOW_THIS_IN_INITIALIZER_LIST(root_view_(new RootView(this))), + contents_view_(contents_view), + is_mouse_button_pressed_(false), + last_mouse_event_was_move_(false), + ALLOW_THIS_IN_INITIALIZER_LIST(close_widget_factory_(this)), + delete_on_destroy_(true) { } -WidgetDelegate* WidgetImpl::GetWidgetDelegate() { - return NULL; +WidgetImpl::~WidgetImpl() { } -void WidgetImpl::SetWidgetDelegate(WidgetDelegate* delegate) { - +void WidgetImpl::InitWithNativeViewParent(gfx::NativeView parent, + const gfx::Rect& bounds) { + native_widget_->InitWithNativeViewParent(parent, bounds); } -void WidgetImpl::SetContentsView(View* view) { - +WidgetImpl* WidgetImpl::GetTopLevelWidgetImpl() const { + NativeWidget* native_widget = + NativeWidget::GetTopLevelNativeWidget(native_widget_->GetNativeView()); + return native_widget->GetWidgetImpl(); } -void WidgetImpl::GetBounds(gfx::Rect* out, bool including_frame) const { +gfx::Rect WidgetImpl::GetWindowScreenBounds() const { + return native_widget_->GetWindowScreenBounds(); +} +gfx::Rect WidgetImpl::GetClientAreaScreenBounds() const { + return native_widget_->GetClientAreaScreenBounds(); } void WidgetImpl::SetBounds(const gfx::Rect& bounds) { - + native_widget_->SetBounds(bounds); } -void WidgetImpl::MoveAbove(Widget* other) { - +void WidgetImpl::SetShape(const gfx::Path& shape) { + native_widget_->SetShape(shape); } -void WidgetImpl::SetShape(gfx::NativeRegion region) { +void WidgetImpl::Show() { + native_widget_->Show(); +} +void WidgetImpl::Hide() { + native_widget_->Hide(); } void WidgetImpl::Close() { + native_widget_->Hide(); + if (close_widget_factory_.empty()) { + MessageLoop::current()->PostTask(FROM_HERE, + close_widget_factory_.NewRunnableMethod(&WidgetImpl::CloseNow)); + } } -void WidgetImpl::CloseNow() { - +void WidgetImpl::MoveAbove(WidgetImpl* other) { + native_widget_->MoveAbove(other->native_widget()); } -void WidgetImpl::Show() { +void WidgetImpl::SetAlwaysOnTop(bool always_on_top) { + NOTIMPLEMENTED(); +} +void WidgetImpl::InvalidateRect(const gfx::Rect& invalid_rect) { + native_widget_->InvalidateRect(invalid_rect); } -void WidgetImpl::Hide() { +ThemeProvider* WidgetImpl::GetThemeProvider() const { + return NULL; +} +FocusManager* WidgetImpl::GetFocusManager() { + return GetTopLevelWidgetImpl()->focus_manager_.get(); } -gfx::NativeView WidgetImpl::GetNativeView() const { - return NULL; +FocusTraversable* WidgetImpl::GetFocusTraversable() const { + return root_view_.get(); } -void WidgetImpl::PaintNow(const gfx::Rect& update_rect) { +//////////////////////////////////////////////////////////////////////////////// +// WidgetImpl, NativeWidgetListener implementation: +void WidgetImpl::OnClose() { + Close(); } -void WidgetImpl::SetOpacity(unsigned char opacity) { +void WidgetImpl::OnDestroy() { + if (delete_on_destroy_) + delete this; +} + +void WidgetImpl::OnDisplayChanged() { + // TODO(beng): } -void WidgetImpl::SetAlwaysOnTop(bool on_top) { +bool WidgetImpl::OnKeyEvent(const KeyEvent& event) { + // find root view. + //return root_view_->OnKeyEvent(event); + return true; } -RootView* WidgetImpl::GetRootView() { - return NULL; +void WidgetImpl::OnMouseCaptureLost() { + if (native_widget_->HasMouseCapture()) { + if (is_mouse_button_pressed_) { + // TODO(beng): Rename to OnMouseCaptureLost(); + root_view_->ProcessMouseDragCanceled(); + } + is_mouse_button_pressed_ = false; + } } -Widget* WidgetImpl::GetRootWidget() const { - return NULL; +bool WidgetImpl::OnMouseEvent(const MouseEvent& event) { + last_mouse_event_was_move_ = false; + switch (event.type()) { + case ui::ET_MOUSE_PRESSED: + if (root_view_->OnMousePressed(event)) { + is_mouse_button_pressed_ = true; + if (!native_widget_->HasMouseCapture()) + native_widget_->SetMouseCapture(); + return true; + } + return false; + case ui::ET_MOUSE_RELEASED: + // TODO(beng): NativeWidgetGtk should not call this function if drag data + // exists, see comment in this function in WidgetGtk. + // Release the capture first, that way we don't get confused if + // OnMouseReleased blocks. + if (native_widget_->HasMouseCapture() && + native_widget_->ShouldReleaseCaptureOnMouseReleased()) { + native_widget_->ReleaseMouseCapture(); + } + is_mouse_button_pressed_ = false; + root_view_->OnMouseReleased(event, false); + return true; + case ui::ET_MOUSE_MOVED: + if (native_widget_->HasMouseCapture() && is_mouse_button_pressed_) { + last_mouse_event_was_move_ = false; + root_view_->OnMouseDragged(event); + } else { + gfx::Point screen_loc(event.location()); + View::ConvertPointToScreen(root_view_.get(), &screen_loc); + if (last_mouse_event_was_move_ && + last_mouse_event_position_ == screen_loc) { + // Don't generate a mouse event for the same location as the last. + return true; + } + last_mouse_event_position_ = screen_loc; + last_mouse_event_was_move_ = true; + root_view_->OnMouseMoved(event); + } + break; + case ui::ET_MOUSE_EXITED: + // TODO(beng): rename to OnMouseExited(event); + root_view_->ProcessOnMouseExited(); + return true; + default: + break; + } + return true; } -bool WidgetImpl::IsVisible() const { - return false; +bool WidgetImpl::OnMouseWheelEvent(const MouseWheelEvent& event) { + // TODO(beng): rename to OnMouseWheel(event); + return !root_view_->ProcessMouseWheelEvent(event); } -bool WidgetImpl::IsActive() const { - return false; +void WidgetImpl::OnNativeWidgetCreated() { + root_view_->SetContentsView(contents_view_); + if (GetTopLevelWidgetImpl() == this) + focus_manager_.reset(new FocusManager(this)); } -bool WidgetImpl::IsAccessibleWidget() const { - return false; +void WidgetImpl::OnPaint(gfx::Canvas* canvas) { + // TODO(beng): replace with root_view_->Paint(canvas); +#if defined(OS_WIN) + root_view_->OnPaint(native_widget_->GetNativeView()); +#endif } -TooltipManager* WidgetImpl::GetTooltipManager() { - return NULL; +void WidgetImpl::OnSizeChanged(const gfx::Size& size) { + root_view_->SetSize(size); } -void WidgetImpl::GenerateMousePressedForView(View* view, - const gfx::Point& point) { +void WidgetImpl::OnNativeFocus(gfx::NativeView focused_view) { + GetFocusManager()->GetWidgetFocusManager()->OnWidgetFocusEvent( + focused_view, native_widget_->GetNativeView()); +} +void WidgetImpl::OnNativeBlur(gfx::NativeView focused_view) { + GetFocusManager()->GetWidgetFocusManager()->OnWidgetFocusEvent( + native_widget_->GetNativeView(), focused_view); } -bool WidgetImpl::GetAccelerator(int cmd_id, ui::Accelerator* accelerator) { - return false; +void WidgetImpl::OnWorkAreaChanged() { + } -Window* WidgetImpl::GetWindow() { - return NULL; +WidgetImpl* WidgetImpl::GetWidgetImpl() { + return const_cast<WidgetImpl*>(const_cast<const WidgetImpl*>(this)); } -const Window* WidgetImpl::GetWindow() const { - return NULL; +const WidgetImpl* WidgetImpl::GetWidgetImpl() const { + return this; } -void WidgetImpl::SetNativeWindowProperty(const char* name, void* value) { +//////////////////////////////////////////////////////////////////////////////// +// WidgetImpl, Widget implementation: (TEMPORARY, TEMPORARY, TEMPORARY!) +void WidgetImpl::Init(gfx::NativeView parent, const gfx::Rect& bounds) { + InitWithNativeViewParent(parent, bounds); } -void* WidgetImpl::GetNativeWindowProperty(const char* name) { - return NULL; +void WidgetImpl::InitWithWidget(Widget* parent, const gfx::Rect& bounds) { + NOTIMPLEMENTED(); } -ThemeProvider* WidgetImpl::GetThemeProvider() const { +WidgetDelegate* WidgetImpl::GetWidgetDelegate() { + NOTIMPLEMENTED(); return NULL; } -ThemeProvider* WidgetImpl::GetDefaultThemeProvider() const { - return NULL; +void WidgetImpl::SetWidgetDelegate(WidgetDelegate* delegate) { + NOTIMPLEMENTED(); } -FocusManager* WidgetImpl::GetFocusManager() { - return NULL; +void WidgetImpl::SetContentsView(View* view) { + NOTIMPLEMENTED(); } -void WidgetImpl::ViewHierarchyChanged(bool is_add, View *parent, - View *child) { +void WidgetImpl::GetBounds(gfx::Rect* out, bool including_frame) const { + NOTIMPLEMENTED(); +} +void WidgetImpl::MoveAbove(Widget* widget) { + NativeWidget* other = + NativeWidget::GetNativeWidgetForNativeView(widget->GetNativeView()); + if (other) + native_widget_->MoveAbove(other); } -bool WidgetImpl::ContainsNativeView(gfx::NativeView native_view) { - return false; +void WidgetImpl::SetShape(gfx::NativeRegion region) { + NOTIMPLEMENTED(); } -//////////////////////////////////////////////////////////////////////////////// -// WidgetImpl, NativeWidgetListener implementation: +gfx::NativeView WidgetImpl::GetNativeView() const { + return native_widget_->GetNativeView(); +} -void WidgetImpl::OnClose() { +void WidgetImpl::PaintNow(const gfx::Rect& update_rect) { + NOTIMPLEMENTED(); +} +void WidgetImpl::SetOpacity(unsigned char opacity) { + NOTIMPLEMENTED(); } -void WidgetImpl::OnDestroy() { +RootView* WidgetImpl::GetRootView() { + return root_view_.get(); +} +Widget* WidgetImpl::GetRootWidget() const { + NOTIMPLEMENTED(); + return NULL; } -void WidgetImpl::OnDisplayChanged() { +bool WidgetImpl::IsVisible() const { + NOTIMPLEMENTED(); + return false; +} +bool WidgetImpl::IsActive() const { + NOTIMPLEMENTED(); + return false; } -bool WidgetImpl::OnKeyEvent(const KeyEvent& event) { +bool WidgetImpl::IsAccessibleWidget() const { + NOTIMPLEMENTED(); return false; } -void WidgetImpl::OnMouseCaptureLost() { +TooltipManager* WidgetImpl::GetTooltipManager() { + NOTIMPLEMENTED(); + return NULL; +} +void WidgetImpl::GenerateMousePressedForView(View* view, + const gfx::Point& point) { + NOTIMPLEMENTED(); } -bool WidgetImpl::OnMouseEvent(const MouseEvent& event) { +bool WidgetImpl::GetAccelerator(int cmd_id, ui::Accelerator* accelerator) { return false; } -bool WidgetImpl::OnMouseWheelEvent(const MouseWheelEvent& event) { - return false; +Window* WidgetImpl::GetWindow() { + return NULL; } -void WidgetImpl::OnNativeWidgetCreated() { +const Window* WidgetImpl::GetWindow() const { + return NULL; +} +void WidgetImpl::SetNativeWindowProperty(const char* name, void* value) { + native_widget_->SetNativeWindowProperty(name, value); } -void WidgetImpl::OnPaint(gfx::Canvas* canvas) { +void* WidgetImpl::GetNativeWindowProperty(const char* name) { + return native_widget_->GetNativeWindowProperty(name); +} +ThemeProvider* WidgetImpl::GetDefaultThemeProvider() const { + NOTIMPLEMENTED(); + return NULL; } -void WidgetImpl::OnSizeChanged(const gfx::Size& size) { +void WidgetImpl::ViewHierarchyChanged(bool is_add, View* parent, View* child) { + NOTIMPLEMENTED(); +} +bool WidgetImpl::ContainsNativeView(gfx::NativeView native_view) { + NOTIMPLEMENTED(); + return false; } -void WidgetImpl::OnNativeFocus(gfx::NativeView focused_view) { +//////////////////////////////////////////////////////////////////////////////// +// WidgetImpl, private: +void WidgetImpl::CloseNow() { + native_widget_->Close(); } -void WidgetImpl::OnNativeBlur(gfx::NativeView focused_view) { +#if !defined(OS_WIN) +//////////////////////////////////////////////////////////////////////////////// +// NativeWidget, public: + +// static +NativeWidget* NativeWidget::CreateNativeWidget( + internal::NativeWidgetListener* listener) { + return NULL; } -void WidgetImpl::OnWorkAreaChanged() { +// static +NativeWidget* NativeWidget::GetNativeWidgetForNativeView( + gfx::NativeView native_view) { + return NULL; +} +// static +NativeWidget* NativeWidget::GetNativeWidgetForNativeWindow( + gfx::NativeWindow native_window) { + return NULL; } -WidgetImpl* WidgetImpl::GetWidgetImpl() const { +// static +NativeWidget* NativeWidget::GetTopLevelNativeWidget( + gfx::NativeView native_view) { return NULL; } -//////////////////////////////////////////////////////////////////////////////// -// WidgetImpl, private: +#endif // !defined(OS_WIN) } // namespace views + diff --git a/views/widget/widget_impl.h b/views/widget/widget_impl.h index 8f00711..3327b5d 100644 --- a/views/widget/widget_impl.h +++ b/views/widget/widget_impl.h @@ -4,49 +4,134 @@ #ifndef VIEWS_WIDGET_WIDGET_IMPL_H_ #define VIEWS_WIDGET_WIDGET_IMPL_H_ -#pragma once +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "ui/gfx/point.h" +#include "views/native_types.h" +#include "views/focus/focus_manager.h" #include "views/widget/native_widget_listener.h" #include "views/widget/widget.h" -namespace views { +namespace gfx { +class Canvas; +class Path; +class Rect; +class Size; +} + +namespace ui { + class ThemeProvider; +} +namespace views { +class FocusManager; +class KeyEvent; +class MouseEvent; +class MouseWheelEvent; class NativeWidget; +class RootView; +class View; //////////////////////////////////////////////////////////////////////////////// // WidgetImpl class // -// An object that represents a native widget that hosts a view hierarchy. -// A WidgetImpl is owned by a NativeWidget implementation. +// Encapsulates the platform-specific rendering, event receiving and widget +// management aspects of the UI framework. // -// TODO(beng): This class should be renamed Widget after the transition to the -// V2 API is complete. +// Owns a RootView and thus a View hierarchy. Can contain child WidgetImpls. +// WidgetImpl is a platform-independent type that communicates with a platform +// or context specific NativeWidget implementation. // -class WidgetImpl : public Widget, - internal::NativeWidgetListener { +// TODO(beng): Consider ownership of this object vs. NativeWidget. +class WidgetImpl : public internal::NativeWidgetListener, + public Widget { public: - WidgetImpl(); + explicit WidgetImpl(View* contents_view); virtual ~WidgetImpl(); + void set_delete_on_destroy(bool delete_on_destroy) { + delete_on_destroy_ = delete_on_destroy; + } + + // Initialization. + void InitWithNativeViewParent(gfx::NativeView parent, + const gfx::Rect& bounds); + + // Returns the topmost WidgetImpl in a hierarchy. + WidgetImpl* GetTopLevelWidgetImpl() const; + + // Returns the bounding rect of the Widget in screen coordinates. + gfx::Rect GetWindowScreenBounds() const; + + // Returns the bounding rect of the Widget's client area, in screen + // coordinates. + gfx::Rect GetClientAreaScreenBounds() const; + + // Sets the bounding rect of the Widget, in the coordinate system of its + // parent. + void SetBounds(const gfx::Rect& bounds); + + void SetShape(const gfx::Path& shape); + + void Show(); + void Hide(); + + void Close(); + + void MoveAbove(WidgetImpl* other); + void SetAlwaysOnTop(bool always_on_top); + + // Causes the specified rectangle to be added to the invalid rectangle for the + // WidgetImpl. + void InvalidateRect(const gfx::Rect& invalid_rect); + + // Returns a ThemeProvider that can be used to provide resources when + // rendering Views associated with this WidgetImpl. + ThemeProvider* GetThemeProvider() const; + + // Returns the FocusManager for this WidgetImpl. Only top-level WidgetImpls + // have FocusManagers. + FocusManager* GetFocusManager(); + + FocusTraversable* GetFocusTraversable() const; + + NativeWidget* native_widget() const { return native_widget_.get(); } + private: + // Overridden from internal::NativeWidgetListener: + virtual void OnClose(); + virtual void OnDestroy(); + virtual void OnDisplayChanged(); + virtual bool OnKeyEvent(const KeyEvent& event); + virtual void OnMouseCaptureLost(); + virtual bool OnMouseEvent(const MouseEvent& event); + virtual bool OnMouseWheelEvent(const MouseWheelEvent& event); + virtual void OnNativeWidgetCreated(); + virtual void OnPaint(gfx::Canvas* canvas); + virtual void OnSizeChanged(const gfx::Size& size); + virtual void OnNativeFocus(gfx::NativeView focused_view); + virtual void OnNativeBlur(gfx::NativeView focused_view); + virtual void OnWorkAreaChanged(); + virtual WidgetImpl* GetWidgetImpl(); + virtual const WidgetImpl* GetWidgetImpl() const; + // Overridden from Widget: + // TODO(beng): THIS IS TEMPORARY, and excludes methods duplicated above in + // new WidgetImpl API. + // TODO(beng): Remove/Merge with WidgetImpl API above. virtual void Init(gfx::NativeView parent, const gfx::Rect& bounds); virtual void InitWithWidget(Widget* parent, const gfx::Rect& bounds); virtual WidgetDelegate* GetWidgetDelegate(); virtual void SetWidgetDelegate(WidgetDelegate* delegate); virtual void SetContentsView(View* view); virtual void GetBounds(gfx::Rect* out, bool including_frame) const; - virtual void SetBounds(const gfx::Rect& bounds); - virtual void MoveAbove(Widget* other); + virtual void MoveAbove(Widget* widget); virtual void SetShape(gfx::NativeRegion region); - virtual void Close(); - virtual void CloseNow(); - virtual void Show(); - virtual void Hide(); virtual gfx::NativeView GetNativeView() const; virtual void PaintNow(const gfx::Rect& update_rect); virtual void SetOpacity(unsigned char opacity); - virtual void SetAlwaysOnTop(bool on_top); virtual RootView* GetRootView(); virtual Widget* GetRootWidget() const; virtual bool IsVisible() const; @@ -60,35 +145,45 @@ class WidgetImpl : public Widget, virtual const Window* GetWindow() const; virtual void SetNativeWindowProperty(const char* name, void* value); virtual void* GetNativeWindowProperty(const char* name); - virtual ThemeProvider* GetThemeProvider() const; virtual ThemeProvider* GetDefaultThemeProvider() const; - virtual FocusManager* GetFocusManager(); virtual void ViewHierarchyChanged(bool is_add, View *parent, View *child); virtual bool ContainsNativeView(gfx::NativeView native_view); - // Overridden from NativeWidgetListener: - virtual void OnClose(); - virtual void OnDestroy(); - virtual void OnDisplayChanged(); - virtual bool OnKeyEvent(const KeyEvent& event); - virtual void OnMouseCaptureLost(); - virtual bool OnMouseEvent(const MouseEvent& event); - virtual bool OnMouseWheelEvent(const MouseWheelEvent& event); - virtual void OnNativeWidgetCreated(); - virtual void OnPaint(gfx::Canvas* canvas); - virtual void OnSizeChanged(const gfx::Size& size); - virtual void OnNativeFocus(gfx::NativeView focused_view); - virtual void OnNativeBlur(gfx::NativeView focused_view); - virtual void OnWorkAreaChanged(); - virtual WidgetImpl* GetWidgetImpl() const; + // Causes the Widget to be destroyed immediately. + void CloseNow(); + + // A NativeWidget implementation. This can be changed dynamically to a + // different implementation during the lifetime of the Widget. + scoped_ptr<NativeWidget> native_widget_; - // Weak. - NativeWidget* native_widget_; + // A RootView that owns the View hierarchy within this Widget. + scoped_ptr<RootView> root_view_; + // TODO(beng): Remove once we upgrade RootView. + View* contents_view_; + + // True when any mouse button is pressed. + bool is_mouse_button_pressed_; + + // The following are used to detect duplicate mouse move events and not + // deliver them. Displaying a window may result in the system generating + // duplicate move events even though the mouse hasn't moved. + bool last_mouse_event_was_move_; + gfx::Point last_mouse_event_position_; + + // Handles closing the Widget after a return to the message loop to allow the + // stack to unwind. + ScopedRunnableMethodFactory<WidgetImpl> close_widget_factory_; + + // True if the Widget should be automatically deleted when it is destroyed. + bool delete_on_destroy_; + + scoped_ptr<FocusManager> focus_manager_; DISALLOW_COPY_AND_ASSIGN(WidgetImpl); }; } // namespace views -#endif // VIEWS_WIDGET_WIDGET_H_ +#endif // VIEWS_WIDGET_WIDGET_IMPL_H_ + diff --git a/views/widget/widget_impl_test_util.cc b/views/widget/widget_impl_test_util.cc new file mode 100644 index 0000000..4339e7e --- /dev/null +++ b/views/widget/widget_impl_test_util.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2011 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 "views/widget/widget_impl_test_util.h" + +#include "views/view.h" +#include "views/widget/native_widget.h" +#include "views/widget/widget_impl.h" + +namespace views { +namespace internal { + +WidgetImpl* CreateWidgetImpl() { + return CreateWidgetImplWithContents(new View); +} + +WidgetImpl* CreateWidgetImplWithContents(View* contents_view) { + WidgetImpl* widget = new WidgetImpl(contents_view); + widget->set_delete_on_destroy(false); + widget->InitWithNativeViewParent(NULL, gfx::Rect(10, 10, 200, 200)); + return widget; +} + +WidgetImpl* CreateWidgetImplWithParent(WidgetImpl* parent) { + WidgetImpl* widget = new WidgetImpl(new View); + widget->set_delete_on_destroy(false); + widget->InitWithNativeViewParent(parent->native_widget()->GetNativeView(), + gfx::Rect(10, 10, 200, 200)); + return widget; +} + +} // namespace internal +} // namespace views diff --git a/views/widget/widget_impl_test_util.h b/views/widget/widget_impl_test_util.h new file mode 100644 index 0000000..5609397 --- /dev/null +++ b/views/widget/widget_impl_test_util.h @@ -0,0 +1,22 @@ +// Copyright (c) 2011 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 VIEWS_WIDGET_WIDGET_IMPL_TEST_UTIL_H_ +#define VIEWS_WIDGET_WIDGET_IMPL_TEST_UTIL_H_ +#pragma once + +namespace views { +class View; +class WidgetImpl; +namespace internal { + +// Create dummy WidgetImpls for use in testing. +WidgetImpl* CreateWidgetImpl(); +WidgetImpl* CreateWidgetImplWithContents(View* contents_view); +WidgetImpl* CreateWidgetImplWithParent(WidgetImpl* parent); + +} // namespace internal +} // namespace views + +#endif // VIEWS_WIDGET_WIDGET_IMPL_TEST_UTIL_H_ diff --git a/views/widget/widget_impl_unittest.cc b/views/widget/widget_impl_unittest.cc new file mode 100644 index 0000000..057ed1d --- /dev/null +++ b/views/widget/widget_impl_unittest.cc @@ -0,0 +1,38 @@ +// Copyright (c) 2011 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 "base/logging.h" +#include "base/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "views/focus/focus_manager.h" +#include "views/widget/native_widget.h" +#include "views/widget/widget_impl.h" +#include "views/widget/widget_impl_test_util.h" + +namespace views { + +class WidgetTest : public testing::Test { + public: + WidgetTest() {} + virtual ~WidgetTest() {} + + private: + DISALLOW_COPY_AND_ASSIGN(WidgetTest); +}; + +TEST_F(WidgetTest, FocusManagerInit_Basic) { + scoped_ptr<WidgetImpl> widget(internal::CreateWidgetImpl()); + EXPECT_TRUE(widget->GetFocusManager() != NULL); +} + +TEST_F(WidgetTest, FocusManagerInit_Nested) { + scoped_ptr<WidgetImpl> parent(internal::CreateWidgetImpl()); + scoped_ptr<WidgetImpl> child( + internal::CreateWidgetImplWithParent(parent.get())); + + EXPECT_EQ(parent->GetFocusManager(), child->GetFocusManager()); +} + +} // namespace views + |