diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-01 22:14:38 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-01 22:14:38 +0000 |
commit | 2a3851ec96ce92e9d51ef2737bf8a7137dad49e3 (patch) | |
tree | a0abc20bc21cef3eda852c0b2556f2ffe2b527be /ui | |
parent | 4258ecebbc45339f390bf874726014eeff2400ed (diff) | |
download | chromium_src-2a3851ec96ce92e9d51ef2737bf8a7137dad49e3.zip chromium_src-2a3851ec96ce92e9d51ef2737bf8a7137dad49e3.tar.gz chromium_src-2a3851ec96ce92e9d51ef2737bf8a7137dad49e3.tar.bz2 |
sFirst cut at an experiment with what a simplified View/Widget API would look like.
Of note:
- Widget is a cross-platform class encapsulating widget-specific state
- NativeWidget interface wraps different implementations of a native widget. Starting with HWND.
- NativeWidget implementation should eventually be swappable at runtime.
- Simpler Event construction directly from a NativeEvent (e.g. MSG struct)
- Simpler View API with fewer, more clearly delineated overrides.
Notes:
- Window* are just empty files for now while I get View/Widget to work.
BUG=none
TEST=see unittests, in development
Review URL: http://codereview.chromium.org/6286013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@73353 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
41 files changed, 4138 insertions, 0 deletions
diff --git a/ui/views/OWNERS b/ui/views/OWNERS new file mode 100644 index 0000000..3ff71eb --- /dev/null +++ b/ui/views/OWNERS @@ -0,0 +1,2 @@ +ben@chromium.org
+sky@chromium.org
diff --git a/ui/views/README.chromium b/ui/views/README.chromium new file mode 100644 index 0000000..7975925 --- /dev/null +++ b/ui/views/README.chromium @@ -0,0 +1,4 @@ +This is a work-in-progress Views prototype that is not currently in use.
+
+Please contact beng with questions.
+
diff --git a/ui/views/demo/main.cc b/ui/views/demo/main.cc new file mode 100644 index 0000000..f3b2763 --- /dev/null +++ b/ui/views/demo/main.cc @@ -0,0 +1,177 @@ +// 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 <windows.h> +#include <atlbase.h> +#include <atlapp.h> +#include <atlcrack.h> +#include <atlmisc.h> + +#include "base/at_exit.h" +#include "base/message_loop.h" +#include "gfx/canvas.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/native_widget_win.h" + +class V2DemoDispatcher : public MessageLoopForUI::Dispatcher { + public: + V2DemoDispatcher() {} + virtual ~V2DemoDispatcher() {} + + private: + // Overridden from MessageLoopForUI::Dispatcher: + virtual bool Dispatch(const MSG& msg) { + TranslateMessage(&msg); + DispatchMessage(&msg); + return true; + } + + DISALLOW_COPY_AND_ASSIGN(V2DemoDispatcher); +}; + +class ColorView : public ui::View { + public: + explicit ColorView(SkColor color) : color_(color) { + } + ColorView() : color_(SK_ColorBLACK) {} + virtual ~ColorView() {} + + protected: + SkColor color() const { return color_; } + void set_color(SkColor color) { color_ = color; } + + private: + // Overridden from ui::View: + virtual void OnPaint(gfx::Canvas* canvas) { + canvas->FillRectInt(color_, 0, 0, width(), height()); + } + virtual bool OnMousePressed(const ui::MouseEvent& event) { + color_ = color_ == SK_ColorBLACK ? SK_ColorWHITE : SK_ColorBLACK; + Invalidate(); + return true; + } + virtual void OnMouseReleased(const ui::MouseEvent& event, bool canceled) { + color_ = color_ == SK_ColorWHITE ? SK_ColorMAGENTA : SK_ColorGREEN; + Invalidate(); + } + virtual void OnMouseMoved(const ui::MouseEvent& event) { + U8CPU r = SkColorGetR(color_); + color_ = SkColorSetRGB(++r % 255, SkColorGetG(color_), + SkColorGetB(color_)); + Invalidate(); + } + virtual bool OnMouseDragged(const ui::MouseEvent& event) { + U8CPU g = SkColorGetG(color_); + color_ = SkColorSetRGB(SkColorGetR(color_), ++g % 255, + SkColorGetB(color_)); + Invalidate(); + return true; + } + + SkColor color_; + + DISALLOW_COPY_AND_ASSIGN(ColorView); +}; + +class FancyPantsView : public ColorView { + public: + FancyPantsView() + : ColorView(SK_ColorMAGENTA), + c1_(new ColorView(SK_ColorGREEN)), + c2_(new ColorView(SK_ColorRED)) { + AddChildView(c1_); + AddChildView(c2_); + } + virtual ~FancyPantsView() {} + + // Overridden from ui::View: + virtual void Layout() { + c1_->SetBounds(20, 20, width() - 40, height() - 40); + c2_->SetBounds(50, 50, 50, 50); + Invalidate(); + } + virtual bool OnMousePressed(const ui::MouseEvent& event) { + old_color_ = color(); + set_color(SK_ColorWHITE); + mouse_offset_ = event.location(); + return true; + } + virtual bool OnMouseDragged(const ui::MouseEvent& event) { + gfx::Rect old_bounds = bounds(); + SetPosition(gfx::Point(event.x() - mouse_offset_.x(), + event.y() - mouse_offset_.y())); + gfx::Rect new_bounds = bounds(); + parent()->InvalidateRect(old_bounds.Union(new_bounds)); + return true; + } + virtual void OnMouseReleased(const ui::MouseEvent& event) { + set_color(old_color_); + } + virtual void OnMouseCaptureLost() { + set_color(SK_ColorYELLOW); + } + + private: + View* c1_; + View* c2_; + + gfx::Point mouse_offset_; + SkColor old_color_; + + DISALLOW_COPY_AND_ASSIGN(FancyPantsView); +}; + + + +class ContentsView : public ColorView { + public: + ContentsView() + : c1_(new ColorView(SK_ColorBLUE)), + c2_(new ColorView(SK_ColorGREEN)), + c3_(new FancyPantsView()), + ColorView(SK_ColorRED) { + set_parent_owned(false); + AddChildView(c1_); + AddChildView(c2_); + c3_->SetPosition(gfx::Point(200, 200)); + AddChildView(c3_); + } + + virtual ~ContentsView() {} + + void Init() { + //c3_->SetHasLayer(true); + } + + private: + // Overridden from ui::View: + virtual void Layout() { + c1_->SetBounds(20, 20, width() - 40, height() - 40); + c2_->SetBounds(50, 50, 50, 50); + c3_->SetSize(gfx::Size(75, 75)); + Invalidate(); + } + + View* c1_; + View* c2_; + FancyPantsView* c3_; +}; + +int main(int argc, char **argv) { + OleInitialize(NULL); + base::AtExitManager exit_manager; + MessageLoop main_message_loop(MessageLoop::TYPE_UI); + + ContentsView cv; + ui::Widget widget(&cv); + widget.InitWithNativeViewParent(NULL, gfx::Rect(20, 20, 400, 400)); + cv.Init(); + widget.Show(); + + V2DemoDispatcher dispatcher; + MessageLoopForUI::current()->Run(&dispatcher); + + OleUninitialize(); +} diff --git a/ui/views/events/context_menu_controller.h b/ui/views/events/context_menu_controller.h new file mode 100644 index 0000000..3b8a0b0 --- /dev/null +++ b/ui/views/events/context_menu_controller.h @@ -0,0 +1,47 @@ +// 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 UI_VIEWS_EVENTS_CONTEXT_MENU_CONTROLLER_H_ +#define UI_VIEWS_EVENTS_CONTEXT_MENU_CONTROLLER_H_ +#pragma once + +namespace gfx { +class Point; +} + +namespace ui { + +class View; + +// ContextMenuController is responsible for showing the context menu for a +// View. To use a ContextMenuController invoke SetContextMenuController on a +// View. When the appropriate user gesture occurs ShowContextMenu is invoked +// on the ContextMenuController. +// +// Setting a ContextMenuController on a View makes the View process mouse +// events. +// +// It is up to subclasses that do their own mouse processing to invoke +// the appropriate ContextMenuController method, typically by invoking super's +// implementation for mouse processing. +// +class ContextMenuController { + public: + // Invoked to show the context menu for the source view. If |is_mouse_gesture| + // is true, |point| is the location of the mouse. If |is_mouse_gesture| is + // false, this method was not invoked by a mouse gesture and |point| is the + // recommended location to show the menu at. + // + // |point| is in screen coordinates. + virtual void ShowContextMenu(View* source, + const gfx::Point& point, + bool is_mouse_gesture) = 0; + + protected: + virtual ~ContextMenuController() {} +}; + +} // namespace ui + +#endif // UI_VIEWS_EVENTS_CONTEXT_MENU_CONTROLLER_H_ diff --git a/ui/views/events/drag_controller.h b/ui/views/events/drag_controller.h new file mode 100644 index 0000000..4352195 --- /dev/null +++ b/ui/views/events/drag_controller.h @@ -0,0 +1,37 @@ +// 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 UI_VIEWS_EVENTS_DRAG_CONTROLLER_H_ +#define UI_VIEWS_EVENTS_DRAG_CONTROLLER_H_ +#pragma once + +namespace ui { + +// DragController is responsible for writing drag data for a View, as well as +// supplying the supported drag operations without having to subclass View. +class DragController { + public: + // Writes the data for the drag. + virtual void WriteDragData(View* sender, + const gfx::Point& press_pt, + OSExchangeData* data) = 0; + + // Returns the supported drag operations (see DragDropTypes for possible + // values). A drag is only started if this returns a non-zero value. + virtual int GetDragOperations(View* sender, const gfx::Point& p) = 0; + + // Returns true if a drag operation can be started. + // |press_pt| represents the coordinates where the mouse was initially + // pressed down. |p| is the current mouse coordinates. + virtual bool CanStartDrag(View* sender, + const gfx::Point& press_pt, + const gfx::Point& p) = 0; + + protected: + virtual ~DragController() {} +}; + +} // namespace ui + +#endif // UI_VIEWS_EVENTS_DRAG_CONTROLLER_H_ diff --git a/ui/views/events/event.cc b/ui/views/events/event.cc new file mode 100644 index 0000000..4c62b8a --- /dev/null +++ b/ui/views/events/event.cc @@ -0,0 +1,44 @@ +// 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 "ui/views/events/event.h" + +#include "ui/views/view.h" + +namespace ui { + +//////////////////////////////////////////////////////////////////////////////// +// Event, protected: + +Event::Event(EventType type, int flags) + : type_(type), + flags_(flags) { +} + +//////////////////////////////////////////////////////////////////////////////// +// LocatedEvent, protected: + +LocatedEvent::LocatedEvent(EventType type, + const gfx::Point& location, + int flags) + : Event(type, flags), + location_(location) { +} + +LocatedEvent::LocatedEvent(const LocatedEvent& other, + View* source, + View* target) + : Event(other.type(), other.flags()) { + location_ = other.location(); + View::ConvertPointToView(source, target, &location_); +} + +//////////////////////////////////////////////////////////////////////////////// +// MouseEvent, public: + +MouseEvent::MouseEvent(const MouseEvent& other, View* source, View* target) + : LocatedEvent(other, source, target) { +} + +} // namespace views diff --git a/ui/views/events/event.h b/ui/views/events/event.h new file mode 100644 index 0000000..fc0a0d6 --- /dev/null +++ b/ui/views/events/event.h @@ -0,0 +1,190 @@ +// 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 UI_VIEWS_EVENTS_EVENT_H_ +#define UI_VIEWS_EVENTS_EVENT_H_ +#pragma once + +#include "base/basictypes.h" +#include "gfx/point.h" +#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/views/native_types.h" + +class OSExchangeData; + +namespace ui { + +class View; + +class Event { + public: + // Event types. + enum EventType { ET_UNKNOWN = 0, + ET_MOUSE_PRESSED, + ET_MOUSE_DRAGGED, + ET_MOUSE_RELEASED, + ET_MOUSE_MOVED, + ET_MOUSE_ENTERED, + ET_MOUSE_EXITED, + ET_KEY_PRESSED, + ET_KEY_RELEASED, + ET_MOUSEWHEEL, + ET_DROP_TARGET_EVENT }; + + // Event flags currently supported. Although this is a "views" + // file, this header is used on non-views platforms (e.g. OSX). For + // example, these EventFlags are used by the automation provider for + // all platforms. + enum EventFlags { EF_CAPS_LOCK_DOWN = 1 << 0, + EF_SHIFT_DOWN = 1 << 1, + EF_CONTROL_DOWN = 1 << 2, + EF_ALT_DOWN = 1 << 3, + EF_LEFT_BUTTON_DOWN = 1 << 4, + EF_MIDDLE_BUTTON_DOWN = 1 << 5, + EF_RIGHT_BUTTON_DOWN = 1 << 6, + EF_COMMAND_DOWN = 1 << 7, // Only useful on OSX + }; + + EventType type() const { return type_; } + int flags() const { return flags_; } + void set_flags(int flags) { flags_ = flags; } + + // The following methods return true if the respective keys were pressed at + // the time the event was created. + bool IsShiftDown() const { return (flags_ & EF_SHIFT_DOWN) != 0; } + bool IsControlDown() const { return (flags_ & EF_CONTROL_DOWN) != 0; } + bool IsCapsLockDown() const { return (flags_ & EF_CAPS_LOCK_DOWN) != 0; } + bool IsAltDown() const { return (flags_ & EF_ALT_DOWN) != 0; } + + // Returns true if the event is any kind of mouse event. + bool IsMouseEvent() const { + return type_ == ET_MOUSE_PRESSED || + type_ == ET_MOUSE_RELEASED || + type_ == ET_MOUSE_MOVED || + type_ == ET_MOUSE_EXITED || + type_ == ET_MOUSEWHEEL; + } + + protected: + Event(EventType type, int flags); + + private: + EventType type_; + int flags_; + + DISALLOW_COPY_AND_ASSIGN(Event); +}; + +class LocatedEvent : public Event { + public: + int x() const { return location_.x(); } + int y() const { return location_.y(); } + const gfx::Point& location() const { return location_; } + + protected: + // Constructors called from subclasses. + + // Simple initialization from cracked metadata. + LocatedEvent(EventType type, const gfx::Point& location, int flags); + + // During event processing, event locations are translated from the + // coordinates of a source View to a target as the tree is descended. This + // translation occurs by constructing a new event from another event object, + // specifying a |source| and |target| View to facilitate coordinate + // conversion. Events that are processed in this manner will have a similar + // constructor that calls into this one. + LocatedEvent(const LocatedEvent& other, View* source, View* target); + + private: + gfx::Point location_; + + DISALLOW_COPY_AND_ASSIGN(LocatedEvent); +}; + +class MouseEvent : public LocatedEvent { + public: + // Flags specific to mouse events + enum MouseEventFlags { + EF_IS_DOUBLE_CLICK = 1 << 16, + EF_IS_NON_CLIENT = 1 << 17 + }; + + explicit MouseEvent(NativeEvent native_event); + + MouseEvent(const MouseEvent& other, View* source, View* target); + + // Conveniences to quickly test what button is down: + bool IsOnlyLeftMouseButton() const { + return (flags() & EF_LEFT_BUTTON_DOWN) && + !(flags() & (EF_MIDDLE_BUTTON_DOWN | EF_RIGHT_BUTTON_DOWN)); + } + bool IsLeftMouseButton() const { + return (flags() & EF_LEFT_BUTTON_DOWN) != 0; + } + bool IsOnlyMiddleMouseButton() const { + return (flags() & EF_MIDDLE_BUTTON_DOWN) && + !(flags() & (EF_LEFT_BUTTON_DOWN | EF_RIGHT_BUTTON_DOWN)); + } + bool IsMiddleMouseButton() const { + return (flags() & EF_MIDDLE_BUTTON_DOWN) != 0; + } + bool IsOnlyRightMouseButton() const { + return (flags() & EF_RIGHT_BUTTON_DOWN) && + !(flags() & (EF_LEFT_BUTTON_DOWN | EF_MIDDLE_BUTTON_DOWN)); + } + bool IsRightMouseButton() const { + return (flags() & EF_RIGHT_BUTTON_DOWN) != 0; + } + + private: + DISALLOW_COPY_AND_ASSIGN(MouseEvent); +}; + +class KeyEvent : public Event { + public: + explicit KeyEvent(NativeEvent native_event); + + KeyboardCode key_code() const { return key_code_; } + + int repeat_count() const { return repeat_count_; } + + private: + KeyboardCode key_code_; + int repeat_count_; + int message_flags_; + + DISALLOW_COPY_AND_ASSIGN(KeyEvent); +}; + +class MouseWheelEvent : public LocatedEvent { + public: + explicit MouseWheelEvent(NativeEvent native_event); + + int offset() const { return offset_; } + + private: + int offset_; + + DISALLOW_COPY_AND_ASSIGN(MouseWheelEvent); +}; + +/* +class DropTargetEvent : public LocatedEvent { + public: + explicit DropTargetEvent(NativeEvent native_event); + + const OSExchangeData& data() const { return data_; } + int source_operations() const { return source_operations_; } + + private: + const OSExchangeData& data_; + int source_operations_; + + DISALLOW_COPY_AND_ASSIGN(DropTargetEvent); +}; +*/ + +} // namespace ui + +#endif // UI_VIEWS_EVENTS_EVENT_H_ diff --git a/ui/views/events/event_win.cc b/ui/views/events/event_win.cc new file mode 100644 index 0000000..10a4b55 --- /dev/null +++ b/ui/views/events/event_win.cc @@ -0,0 +1,195 @@ +// 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 "ui/views/events/event.h" + +#include <windowsx.h> + +#include "base/logging.h" +#include "ui/base/keycodes/keyboard_code_conversion_win.h" + +namespace ui { + +namespace { + +// Returns a mask corresponding to the set of modifier keys that are currently +// pressed. Windows key messages don't come with control key state as parameters +// as with mouse messages, so we need to explicitly ask for these states. +int GetKeyStateFlags() { + int flags = 0; + if (GetKeyState(VK_MENU) & 0x80) + flags |= Event::EF_ALT_DOWN; + if (GetKeyState(VK_SHIFT) & 0x80) + flags |= Event::EF_SHIFT_DOWN; + if (GetKeyState(VK_CONTROL) & 0x80) + flags |= Event::EF_CONTROL_DOWN; + return flags; +} + +// Convert windows message identifiers to Event types. +Event::EventType EventTypeFromNative(NativeEvent native_event) { + switch (native_event.message) { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + return Event::ET_KEY_PRESSED; + case WM_KEYUP: + case WM_SYSKEYUP: + return Event::ET_KEY_RELEASED; + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_NCLBUTTONDOWN: + case WM_NCMBUTTONDOWN: + case WM_NCRBUTTONDOWN: + case WM_RBUTTONDOWN: + return Event::ET_MOUSE_PRESSED; + case WM_LBUTTONDBLCLK: + case WM_LBUTTONUP: + case WM_MBUTTONDBLCLK: + case WM_MBUTTONUP: + case WM_NCLBUTTONDBLCLK: + case WM_NCLBUTTONUP: + case WM_NCMBUTTONDBLCLK: + case WM_NCMBUTTONUP: + case WM_NCRBUTTONDBLCLK: + case WM_NCRBUTTONUP: + case WM_RBUTTONDBLCLK: + case WM_RBUTTONUP: + return Event::ET_MOUSE_RELEASED; + case WM_MOUSEMOVE: + case WM_NCMOUSEMOVE: + return Event::ET_MOUSE_MOVED; + case WM_MOUSEWHEEL: + return Event::ET_MOUSEWHEEL; + case WM_MOUSELEAVE: + case WM_NCMOUSELEAVE: + return Event::ET_MOUSE_EXITED; + default: + NOTREACHED(); + } + return Event::ET_UNKNOWN; +} + +bool IsClientMouseEvent(NativeEvent native_event) { + return native_event.message == WM_MOUSELEAVE || + (native_event.message >= WM_MOUSEFIRST && + native_event.message <= WM_MOUSELAST); +} + +bool IsNonClientMouseEvent(NativeEvent native_event) { + return native_event.message == WM_NCMOUSELEAVE || + (native_event.message >= WM_NCMOUSEMOVE && + native_event.message <= WM_NCMBUTTONDBLCLK); +} + +gfx::Point MousePositionFromNative(NativeEvent native_event) { + if (IsClientMouseEvent(native_event)) { + // Client message. The position is contained in the LPARAM. + return gfx::Point(GET_X_LPARAM(native_event.lParam), + GET_Y_LPARAM(native_event.lParam)); + } + DCHECK(IsNonClientMouseEvent(native_event)); + // Non-client message. The position is contained in a POINTS structure in + // LPARAM, and is in screen coordinates so we have to convert to client. + POINT native_point = { GET_X_LPARAM(native_event.lParam), + GET_Y_LPARAM(native_event.lParam) }; + ScreenToClient(native_event.hwnd, &native_point); + return gfx::Point(native_point); +} + +int MouseEventFlagsFromNative(NativeEvent native_event) { + int flags = 0; + + // Check if the event occurred in the non-client area. + if (IsNonClientMouseEvent(native_event)) + flags |= MouseEvent::EF_IS_NON_CLIENT; + + // Check for double click events. + switch (native_event.message) { + case WM_NCLBUTTONDBLCLK: + case WM_NCMBUTTONDBLCLK: + case WM_NCRBUTTONDBLCLK: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + flags |= MouseEvent::EF_IS_DOUBLE_CLICK; + break; + } + + // Check for pressed buttons. + if (IsClientMouseEvent(native_event)) { + if (native_event.wParam & MK_LBUTTON) + flags |= Event::EF_LEFT_BUTTON_DOWN; + if (native_event.wParam & MK_MBUTTON) + flags |= Event::EF_MIDDLE_BUTTON_DOWN; + if (native_event.wParam & MK_RBUTTON) + flags |= Event::EF_RIGHT_BUTTON_DOWN; + } else if (IsNonClientMouseEvent(native_event)) { + switch (native_event.message) { + case WM_NCLBUTTONDOWN: + flags |= Event::EF_LEFT_BUTTON_DOWN; + break; + case WM_NCMBUTTONDOWN: + flags |= Event::EF_MIDDLE_BUTTON_DOWN; + break; + case WM_NCRBUTTONDOWN: + flags |= Event::EF_RIGHT_BUTTON_DOWN; + break; + } + } + + // Finally make sure the key state flags are included. + return flags | GetKeyStateFlags(); +} + +int MouseWheelEventFlagsFromNative(NativeEvent native_event) { + int native_flags = GET_KEYSTATE_WPARAM(native_event.wParam); + int flags = 0; + if (native_flags & MK_CONTROL) + flags |= Event::EF_CONTROL_DOWN; + if (native_flags & MK_SHIFT) + flags |= Event::EF_SHIFT_DOWN; + if (GetKeyState(VK_MENU) < 0) + flags |= Event::EF_ALT_DOWN; + if (native_flags & MK_LBUTTON) + flags |= Event::EF_LEFT_BUTTON_DOWN; + if (native_flags & MK_MBUTTON) + flags |= Event::EF_MIDDLE_BUTTON_DOWN; + if (native_flags & MK_RBUTTON) + flags |= Event::EF_RIGHT_BUTTON_DOWN; + return flags; +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// KeyEvent, public: + +KeyEvent::KeyEvent(NativeEvent native_event) + : Event(EventTypeFromNative(native_event), GetKeyStateFlags()), + key_code_(KeyboardCodeForWindowsKeyCode(native_event.wParam)), + repeat_count_(native_event.lParam & 0xFFFF), + message_flags_((native_event.lParam & 0xFFFF0000) >> 16) { +} + +//////////////////////////////////////////////////////////////////////////////// +// MouseEvent, public: + +MouseEvent::MouseEvent(NativeEvent native_event) + : LocatedEvent(EventTypeFromNative(native_event), + MousePositionFromNative(native_event), + MouseEventFlagsFromNative(native_event)) { +} + +//////////////////////////////////////////////////////////////////////////////// +// MouseWheelEvent, public: + +MouseWheelEvent::MouseWheelEvent(NativeEvent native_event) + : LocatedEvent(ET_MOUSEWHEEL, + MousePositionFromNative(native_event), + MouseWheelEventFlagsFromNative(native_event)), + offset_(GET_WHEEL_DELTA_WPARAM(native_event.wParam)) { +} + +} // namespace ui + diff --git a/ui/views/layout/fill_layout.cc b/ui/views/layout/fill_layout.cc new file mode 100644 index 0000000..5c603c7 --- /dev/null +++ b/ui/views/layout/fill_layout.cc @@ -0,0 +1,37 @@ +// 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 "ui/views/layout/fill_layout.h" + +#include "base/logging.h" +#include "ui/views/view.h" + +namespace ui { + +//////////////////////////////////////////////////////////////////////////////// +// FillLayout, public: + +FillLayout::FillLayout() { +} + +FillLayout::~FillLayout() { +} + +//////////////////////////////////////////////////////////////////////////////// +// FillLayout, LayoutManager implementation: + +void FillLayout::Layout(View* host) { + if (host->child_count() == 0) + return; + + View* child = host->GetChildViewAt(0); + child->SetBounds(0, 0, host->width(), host->height()); +} + +gfx::Size FillLayout::GetPreferredSize(View* host) { + DCHECK(host->child_count() == 1); + return host->GetChildViewAt(0)->GetPreferredSize(); +} + +} // namespace ui diff --git a/ui/views/layout/fill_layout.h b/ui/views/layout/fill_layout.h new file mode 100644 index 0000000..559f2e8 --- /dev/null +++ b/ui/views/layout/fill_layout.h @@ -0,0 +1,35 @@ +// 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 UI_VIEWS_LAYOUT_FILL_LAYOUT_H_ +#define UI_VIEWS_LAYOUT_FILL_LAYOUT_H_ +#pragma once + +#include "base/logging.h" +#include "ui/views/layout/layout_manager.h" + +namespace ui { + +//////////////////////////////////////////////////////////////////////////////// +// FillLayout class +// +// A simple LayoutManager that compels a single view to fit its parent. +// +class FillLayout : public LayoutManager { + public: + FillLayout(); + virtual ~FillLayout(); + + // Overridden from LayoutManager: + virtual void Layout(View* host); + virtual gfx::Size GetPreferredSize(View* host); + + private: + DISALLOW_COPY_AND_ASSIGN(FillLayout); +}; + +} // namespace ui + +#endif // UI_VIEWS_LAYOUT_FILL_LAYOUT_H_ + diff --git a/ui/views/layout/layout_manager.cc b/ui/views/layout/layout_manager.cc new file mode 100644 index 0000000..e3ac839 --- /dev/null +++ b/ui/views/layout/layout_manager.cc @@ -0,0 +1,15 @@ +// 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 "ui/views/layout/layout_manager.h" + +#include "ui/views/view.h" + +namespace ui { + +int LayoutManager::GetPreferredHeightForWidth(View* host, int width) { + return GetPreferredSize(host).height(); +} + +} // namespace ui diff --git a/ui/views/layout/layout_manager.h b/ui/views/layout/layout_manager.h new file mode 100644 index 0000000..d985d0d --- /dev/null +++ b/ui/views/layout/layout_manager.h @@ -0,0 +1,57 @@ +// 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 UI_VIEWS_LAYOUT_LAYOUT_MANAGER_H_ +#define UI_VIEWS_LAYOUT_LAYOUT_MANAGER_H_ +#pragma once + +namespace gfx { +class Size; +} + +namespace ui { + +class View; + +//////////////////////////////////////////////////////////////////////////////// +// LayoutManager interface +// +// An interface implemented by an object that manages sizing of a View's +// children. +// +class LayoutManager { + public: + virtual ~LayoutManager() {} + + // Notification that this LayoutManager has been installed on a particular + // host. + virtual void Installed(View* host) {} + + // Notification that this LayoutManager has been uninstalled on a particular + // host. + virtual void Uninstalled(View* host) {} + + // Lay out the children of |host| according to implementation-specific + // heuristics. The graphics used during painting is provided to allow for + // string sizing. + virtual void Layout(View* host) = 0; + + // Return the preferred size which is the size required to give each + // children their respective preferred size. + virtual gfx::Size GetPreferredSize(View* host) = 0; + + // Returns the preferred height for the specified width. The default + // implementation returns the value from GetPreferredSize. + virtual int GetPreferredHeightForWidth(View* host, int width); + + // Notification that a view has been added. + virtual void ViewAdded(View* host, View* view) {} + + // Notification that a view has been removed. + virtual void ViewRemoved(View* host, View* view) {} +}; + +} // namespace ui + +#endif // UI_VIEWS_LAYOUT_LAYOUT_MANAGER_H_ diff --git a/ui/views/native_types.h b/ui/views/native_types.h new file mode 100644 index 0000000..c17a1bb --- /dev/null +++ b/ui/views/native_types.h @@ -0,0 +1,20 @@ +// 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 UI_VIEWS_NATIVE_TYPES_H_ +#define UI_VIEWS_NATIVE_TYPES_H_ +#pragma once + +#include "gfx/native_widget_types.h" + +namespace ui { + +#if defined(OS_WIN) +typedef MSG NativeEvent; +#endif + +} // namespace ui + +#endif // UI_VIEWS_NATIVE_TYPES_H_ + diff --git a/ui/views/rendering/border.cc b/ui/views/rendering/border.cc new file mode 100644 index 0000000..34493e6 --- /dev/null +++ b/ui/views/rendering/border.cc @@ -0,0 +1,69 @@ +// 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 "ui/views/rendering/border.h" + +#include "gfx/canvas.h" +#include "ui/views/view.h" + +namespace ui { + +namespace internal { + +class SolidColorBorder : public Border { + public: + SolidColorBorder(int thickness, SkColor color) : color_(color) { + set_insets(gfx::Insets(thickness, thickness, thickness, thickness)); + } + virtual ~SolidColorBorder() { + } + + // Overridden from Border: + virtual void Paint(const View* view, gfx::Canvas* canvas) const { + canvas->FillRectInt(color_, 0, 0, view->width(), insets().top()); + canvas->FillRectInt(color_, 0, 0, insets().left(), view->height()); + canvas->FillRectInt(color_, 0, view->height() - insets().bottom(), + view->width(), insets().bottom()); + canvas->FillRectInt(color_, view->width() - insets().right(), 0, + insets().right(), view->height()); + } + + private: + SkColor color_; + + DISALLOW_COPY_AND_ASSIGN(SolidColorBorder); +}; + +} + +//////////////////////////////////////////////////////////////////////////////// +// Border, public: + +Border::~Border() { +} + +// static +Border* Border::CreateSolidBorder(int thickness, SkColor color) { + return new internal::SolidColorBorder(thickness, color); +} + +// static +Border* Border::CreateTransparentBorder(const gfx::Insets& insets) { + Border* b = new Border; + b->set_insets(insets); + return b; +} + +void Border::Paint(const View* view, gfx::Canvas* canvas) const { + // Nothing to do. +} + +//////////////////////////////////////////////////////////////////////////////// +// Border, private: + +Border::Border() { +} + +} // namespace ui + diff --git a/ui/views/rendering/border.h b/ui/views/rendering/border.h new file mode 100644 index 0000000..e1b196f --- /dev/null +++ b/ui/views/rendering/border.h @@ -0,0 +1,50 @@ +// 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 UI_VIEWS_BORDER_H_ +#define UI_VIEWS_BORDER_H_ + +#include "base/logging.h" +#include "gfx/insets.h" +#include "third_party/skia/include/core/SkColor.h" + +namespace gfx { +class Canvas; +} + +namespace ui { + +class View; + +//////////////////////////////////////////////////////////////////////////////// +// Border class +// +// A class that provides padding for a View. Subclass to provide custom +// rendering of the Border. Insets determine the size of border. +// +class Border { + public: + virtual ~Border(); + + // Create various common border types. + static Border* CreateSolidBorder(int thickness, SkColor color); + static Border* CreateTransparentBorder(const gfx::Insets& insets); + + gfx::Insets insets() const { return insets_; } + void set_insets(const gfx::Insets& insets) { insets_ = insets; } + + virtual void Paint(const View* view, gfx::Canvas* canvas) const; + + protected: + Border(); + + private: + gfx::Insets insets_; + + DISALLOW_COPY_AND_ASSIGN(Border); +}; + +} // namespace ui + +#endif // UI_VIEWS_BORDER_H_ diff --git a/ui/views/rendering/border_unittest.cc b/ui/views/rendering/border_unittest.cc new file mode 100644 index 0000000..52e62bb --- /dev/null +++ b/ui/views/rendering/border_unittest.cc @@ -0,0 +1,63 @@ +// 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 <algorithm> + +#include "gfx/canvas.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/rendering/border.h" +#include "ui/views/view.h" + +namespace ui { + +class BorderTest : public testing::Test { + public: + BorderTest() {} + virtual ~BorderTest() {} + + private: + DISALLOW_COPY_AND_ASSIGN(BorderTest); +}; + +class TestBorder : public Border { + public: + TestBorder() : painted_(false) {} + + bool painted() const { return painted_; } + + // Overridden from Border: + virtual void Paint(const View* view, gfx::Canvas* canvas) const { + painted_ = true; + } + + private: + mutable bool painted_; + + DISALLOW_COPY_AND_ASSIGN(TestBorder); +}; + +TEST_F(BorderTest, Basic) { + const int kViewSize = 100; + View v; + v.SetBounds(10, 10, kViewSize, kViewSize); + + // With no border, the content size is the view size. + EXPECT_EQ(gfx::Rect(0, 0, kViewSize, kViewSize), v.GetContentsBounds()); + + const int kViewInset = 10; + v.SetBorder(Border::CreateTransparentBorder( + gfx::Insets(kViewInset, kViewInset, kViewInset, kViewInset))); + + // With the border, the content bounds are inset by the border's insets. + EXPECT_EQ(gfx::Rect(kViewInset, kViewInset, kViewSize - 2 * kViewInset, + kViewSize - 2 * kViewInset), + v.GetContentsBounds()); + + TestBorder* border = new TestBorder; + v.SetBorder(border); + v.OnPaint(NULL); + EXPECT_TRUE(border->painted()); +} + +} // namespace ui diff --git a/ui/views/run_all_unittests.cc b/ui/views/run_all_unittests.cc new file mode 100644 index 0000000..d4dfd39 --- /dev/null +++ b/ui/views/run_all_unittests.cc @@ -0,0 +1,19 @@ +// 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/test/test_suite.h" + +class V2TestSuite : public base::TestSuite { + public: + V2TestSuite(int argc, char** argv) : base::TestSuite(argc, argv) {} + + protected: + virtual void Initialize() { + base::TestSuite::Initialize(); + } +}; + +int main(int argc, char **argv) { + return V2TestSuite(argc, argv).Run(); +} diff --git a/ui/views/view.cc b/ui/views/view.cc new file mode 100644 index 0000000..5057afe --- /dev/null +++ b/ui/views/view.cc @@ -0,0 +1,629 @@ +// 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 "ui/views/view.h" + +#include <algorithm> +#include <functional> + +#include "gfx/canvas.h" +#include "gfx/point.h" +#include "gfx/size.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/views/events/context_menu_controller.h" +#include "ui/views/events/drag_controller.h" +#include "ui/views/layout/layout_manager.h" +#include "ui/views/rendering/border.h" +#include "ui/views/widget/widget.h" + +namespace ui { + +namespace { + +// Saves gfx::Canvas state upon construction and automatically restores it when +// it goes out of scope. +class ScopedCanvasState { + public: + explicit ScopedCanvasState(gfx::Canvas* canvas) : canvas_(canvas) { + canvas_->Save(); + } + ~ScopedCanvasState() { + canvas_->Restore(); + } + + private: + gfx::Canvas* canvas_; + DISALLOW_COPY_AND_ASSIGN(ScopedCanvasState); +}; + +bool ExceededDragThreshold(const gfx::Point& press_point, + const gfx::Point& event_point) { + // TODO(beng): implement + return true; +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// View, public: + +View::View() + : parent_(NULL), + parent_owned_(true), + visible_(true), + enabled_(true), + id_(-1), + group_(-1), + focusable_(false), + context_menu_controller_(NULL), + drag_controller_(NULL) { +} + +View::~View() { + if (parent_) + parent_->RemoveChildView(this); + + ViewVector::const_iterator it = children_.begin(); + for (; it != children_.end(); ++it) { + (*it)->parent_ = NULL; + if ((*it)->parent_owned()) + delete *it; + } +} + +// Size and disposition -------------------------------------------------------- + +void View::SetBounds(int x, int y, int width, int height) { + SetBoundsRect(gfx::Rect(x, y, std::max(0, width), std::max(0, height))); +} + +void View::SetBoundsRect(const gfx::Rect& bounds) { + gfx::Rect old_bounds = bounds_; + bounds_ = bounds; + // TODO(beng): investigate usage of needs_layout_ in old View code. + if (old_bounds != bounds_) { + OnBoundsChanged(); + Layout(); + } +} + +gfx::Rect View::GetVisibleBounds() const { + // TODO(beng): + return bounds(); +} + +void View::SetSize(const gfx::Size& size) { + SetBounds(x(), y(), size.width(), size.height()); +} + +void View::SetPosition(const gfx::Point& position) { + SetBounds(position.x(), position.y(), width(), height()); +} + +void View::SetBorder(Border* border) { + border_.reset(border); +} + +gfx::Rect View::GetContentsBounds() const { + if (border_.get()) { + return gfx::Rect( + border_->insets().left(), border_->insets().top(), + width() - border_->insets().right() - border_->insets().left(), + height() - border_->insets().bottom() - border_->insets().top()); + } + return gfx::Rect(0, 0, width(), height()); +} + +void View::OnBoundsChanged() { +} + +gfx::Size View::GetPreferredSize() const { + return gfx::Size(); +} + +gfx::Size View::GetMinimumSize() const { + return GetPreferredSize(); +} + +void View::SetLayoutManager(LayoutManager* layout_manager) { + layout_manager_.reset(layout_manager); +} + +void View::Layout() { + if (layout_manager_.get()) { + // Layout Manager handles laying out children. + layout_manager_->Layout(this); + } else { + // We handle laying out our own children. + ViewVector::iterator it = children_.begin(); + for (; it != children_.end(); ++it) + (*it)->Layout(); + } + // TODO(beng): needs_layout_? SchedulePaint()? +} + +void View::SetVisible(bool visible) { + if (visible != visible_) { + visible_ = visible; + + // InvaldateRect() checks for view visibility before proceeding, so we need + // to ask the parent to invalidate our bounds. + if (parent_) + parent_->InvalidateRect(bounds_); + } +} + +void View::SetEnabled(bool enabled) { + if (enabled != enabled_) { + enabled_ = enabled; + Invalidate(); + } +} + +// Coordinate conversion ------------------------------------------------------- + +// static +void View::ConvertPointToView(View* source, View* target, gfx::Point* point) { + View* inner = NULL; + View* outer = NULL; + if (source->Contains(target)) { + inner = target; + outer = source; + } else if (target->Contains(source)) { + inner = source; + outer = target; + } // Note that we cannot do a plain "else" here since |source| and |target| + // may be in different hierarchies with no relation. + + if (inner && outer) { + gfx::Point offset; + View* temp = inner; + while (temp != outer) { + offset.Offset(temp->x(), temp->y()); + temp = temp->parent(); + } + // When target is contained by source, we need to subtract the offset. + // When source is contained by target, we need to add the fofset. + int multiplier = inner == target ? -1 : 1; + point->Offset(multiplier * offset.x(), multiplier * offset.y()); + } +} + +// static +void View::ConvertPointToScreen(View* source, gfx::Point* point) { + Widget* widget = source->GetWidget(); + if (widget) { + ConvertPointToWidget(source, point); + gfx::Rect r = widget->GetClientAreaScreenBounds(); + point->Offset(r.x(), r.y()); + } +} + +// static +void View::ConvertPointToWidget(View* source, gfx::Point* point) { + for (View* v = source; v; v = v->parent()) + point->Offset(v->x(), v->y()); +} + +// Tree operations ------------------------------------------------------------- + +Widget* View::GetWidget() const { + return parent_ ? parent_->GetWidget() : NULL; +} + +void View::AddChildView(View* view) { + AddChildViewAt(view, children_.size()); +} + +void View::AddChildViewAt(View* view, size_t index) { + CHECK(view != this) << "A view cannot be its own child."; + + // Remove the child from its current parent if any. + if (view->parent()) + view->parent()->RemoveChildView(view); + + children_.insert(children_.begin() + index, view); + view->parent_ = this; + + // Notify the hierarchy. + NotifyHierarchyChanged(this, view, true); + + // TODO(beng): Notify other objects like tooltip, layout manager, etc. + // Figure out RegisterChildrenForVisibleBoundsNotification. +} + +View* View::RemoveChildView(View* view) { + ViewVector::iterator it = find(children_.begin(), children_.end(), view); + if (it != children_.end()) { + View* old_parent = view->parent_; + view->parent_ = NULL; + children_.erase(it); + NotifyHierarchyChanged(old_parent, view, false); + } + + // TODO(beng): Notify other objects like tooltip, layout manager, etc. + return view; +} + +void View::RemoveAllChildViews(bool delete_children) { + // TODO(beng): use for_each. + ViewVector::iterator it = children_.begin(); + while (it != children_.end()) { + View* v = RemoveChildView(*it); + if (delete_children) + delete v; + // TODO(beng): view deletion is actually more complicated in the old view.cc + // figure out why. (it uses a ScopedVector to accumulate a list + // of views to delete). + } +} + +View* View::GetChildViewAt(size_t index) { + CHECK(index < child_count()); + return children_[index]; +} + +bool View::Contains(View* child) { + while (child) { + if (child == this) + return true; + child = child->parent(); + } + return false; +} + +View* View::GetViewForPoint(const gfx::Point& point) const { + ViewVector::const_reverse_iterator it = children_.rbegin(); + for (; it != children_.rend(); ++it) { + View* child = *it; + if (!child->visible()) + continue; + + gfx::Point point_in_child_coords(point); + View::ConvertPointToView(const_cast<View*>(this), child, + &point_in_child_coords); + if (child->HitTest(point_in_child_coords)) + return child->GetViewForPoint(point_in_child_coords); + } + return const_cast<View*>(this); +} + +bool View::HitTest(const gfx::Point& point) const { + // TODO(beng): Hit test mask support. + return gfx::Rect(0, 0, width(), height()).Contains(point); +} + +View* View::GetViewById(int id) const { + if (id_ == id) + return const_cast<View*>(this); + ViewVector::const_iterator it = children_.begin(); + for (; it != children_.end(); ++it) { + View* view = (*it)->GetViewById(id); + if (view) + return view; + } + return NULL; +} + +void View::GetViewsWithGroup(int group, ViewVector* vec) const { + if (group_ == group) + vec->push_back(const_cast<View*>(this)); + ViewVector::const_iterator it = children_.begin(); + for (; it != children_.end(); ++it) + (*it)->GetViewsWithGroup(group, vec); +} + +void View::OnViewAdded(View* parent, View* child) { +} + +void View::OnViewRemoved(View* parent, View* child) { +} + +void View::OnViewAddedToWidget() { +} + +void View::OnViewRemovedFromWidget() { +} + +// Accelerators ---------------------------------------------------------------- + +void View::AddAccelerator(const Accelerator& accelerator) { +} + +void View::RemoveAccelerator(const Accelerator& accelerator) { +} + +void View::RemoveAllAccelerators() { +} + +bool View::OnAcceleratorPressed(const Accelerator& accelerator) { + return false; +} + +// Focus ----------------------------------------------------------------------- + +FocusManager* View::GetFocusManager() const { + return NULL; +} + +FocusTraversable* View::GetFocusTraversable() const { + return NULL; +} + +View* View::GetNextFocusableView() const { + return NULL; +} + +View* View::GetPreviousFocusableView() const { + return NULL; +} + +bool View::SkipDefaultKeyEventProcessing(const KeyEvent& event) const { + return false; +} + +bool View::IsFocusable() const { + return false; +} + +bool View::HasFocus() const { + return false; +} + +void View::RequestFocus() { +} + +void View::OnFocus(/* const FocusEvent& event */) { +} + +void View::OnBlur() { +} + +// Input ----------------------------------------------------------------------- + +bool View::OnKeyPressed(const KeyEvent& event) { + return true; +} + +bool View::OnKeyReleased(const KeyEvent& event) { + return true; +} + +bool View::OnMouseWheel(const MouseWheelEvent& event) { + return true; +} + +bool View::OnMousePressed(const MouseEvent& event) { + return true; +} + +bool View::OnMouseDragged(const MouseEvent& event) { + return true; +} + +void View::OnMouseReleased(const MouseEvent& event) { + +} + +void View::OnMouseCaptureLost() { + +} + +void View::OnMouseMoved(const MouseEvent& event) { + +} + +void View::OnMouseEntered(const MouseEvent& event) { + +} + +void View::OnMouseExited(const MouseEvent& event) { + +} + +gfx::NativeCursor View::GetCursorForPoint(const gfx::Point& point) { + return NULL; +} + +// Painting -------------------------------------------------------------------- + +void View::Invalidate() { + InvalidateRect(gfx::Rect(0, 0, width(), height())); +} + +void View::InvalidateRect(const gfx::Rect& invalid_rect) { + if (!visible_) + return; + + if (parent_) { + gfx::Rect r = invalid_rect; + r.Offset(bounds_.origin()); + parent_->InvalidateRect(r); + } +} + +void View::Paint(gfx::Canvas* canvas) { + // Invisible views are not painted. + if (!visible_) + return; + + ScopedCanvasState canvas_state(canvas); + if (canvas->ClipRectInt(x(), y(), width(), height())) { + canvas->TranslateInt(x(), y()); + // TODO(beng): RTL + ScopedCanvasState canvas_state(canvas); + OnPaint(canvas); + PaintChildren(canvas); + } +} + +void View::PaintChildren(gfx::Canvas* canvas) { + // TODO(beng): use for_each. + // std::for_each(children_.begin(), children_.end(), + // std::bind2nd(std::mem_fun_ref(&View::Paint), canvas)); + ViewVector::iterator it = children_.begin(); + for (; it != children_.end(); ++it) + (*it)->Paint(canvas); +} + +void View::OnPaint(gfx::Canvas* canvas) { + // TODO(beng): investigate moving these function calls to Paint(). + OnPaintBackground(canvas); + OnPaintFocusBorder(canvas); + OnPaintBorder(canvas); +} + +void View::OnPaintBackground(gfx::Canvas* canvas) { +} + +void View::OnPaintBorder(gfx::Canvas* canvas) { + if (border_.get()) + border_->Paint(const_cast<const View*>(this), canvas); +} + +void View::OnPaintFocusBorder(gfx::Canvas* canvas) { +} + +// Resources ------------------------------------------------------------------- + +ThemeProvider* View::GetThemeProvider() const { + Widget* widget = GetWidget(); + return widget ? widget->GetThemeProvider() : NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// View, private: + +void View::DragInfo::Reset() { + possible_drag = false; + press_point = gfx::Point(); +} + +void View::DragInfo::PossibleDrag(const gfx::Point& point) { + possible_drag = true; + press_point = point; +} + +// Drag & Drop ----------------------------------------------------------------- + +int View::GetDragOperations(const gfx::Point& point) { + return drag_controller_ ? + drag_controller_->GetDragOperations(const_cast<View*>(this), point) : + DragDropTypes::DRAG_NONE; +} + +void View::WriteDragData(const gfx::Point& point, OSExchangeData* data) { + drag_controller_->WriteDragData(this, point, data); +} + +void View::StartShellDrag(const MouseEvent& event, + const gfx::Point& press_point) { + // TODO(beng): system stuff. +} + +// RootView API ---------------------------------------------------------------- + +bool View::MousePressed(const MouseEvent& event, DragInfo* drag_info) { + bool handled = OnMousePressed(event); + // TODO(beng): deal with view deletion, see ProcessMousePressed() in old code. + if (!enabled_) + return handled; + + int drag_operations = + enabled_ && event.IsOnlyLeftMouseButton() && HitTest(event.location()) ? + GetDragOperations(event.location()) : DragDropTypes::DRAG_NONE; + if (drag_operations != DragDropTypes::DRAG_NONE) { + drag_info->PossibleDrag(event.location()); + return true; + } + bool has_context_menu = event.IsRightMouseButton() ? + !!context_menu_controller_ : NULL; + return has_context_menu || handled; +} + +bool View::MouseDragged(const MouseEvent& event, DragInfo* drag_info) { + if (drag_info->possible_drag && + ExceededDragThreshold(drag_info->press_point, event.location())) { + if (!drag_controller_ || + drag_controller_->CanStartDrag(this, drag_info->press_point, + event.location())) { + StartShellDrag(event, drag_info->press_point); + } + } else { + if (OnMouseDragged(event)) + return true; + } + // TODO(beng): Handle view deletion from OnMouseDragged(). + return !!context_menu_controller_ || drag_info->possible_drag; +} + +void View::MouseReleased(const MouseEvent& event) { + OnMouseReleased(event); + // TODO(beng): Handle view deletion from OnMouseReleased(). + if (context_menu_controller_ && event.IsOnlyRightMouseButton()) { + gfx::Point location(event.location()); + if (HitTest(location)) { + ConvertPointToScreen(this, &location); + context_menu_controller_->ShowContextMenu(this, location, true); + } + } +} + +// Tree operations ------------------------------------------------------------- + +void View::NotifyHierarchyChanged(View* parent, View* child, bool is_add) { + // Notify the child. Note that we call GetWidget() on the parent, not the + // child, since this method is called after the child is already removed from + // the hierarchy when |is_add| is false and so child->GetWidget() will always + // return NULL. + bool has_widget = parent->GetWidget() != NULL; + CallViewNotification(child, parent, child, is_add, has_widget); + + // Notify the hierarchy up. + NotifyHierarchyChangedUp(parent, child, is_add); + + // Notify the hierarchy down. + if (!is_add) { + // Because |child| has already been removed from |parent|'s child list, we + // need to notify its hierarchy manually. + child->NotifyHierarchyChangedDown(parent, child, is_add, has_widget); + } + NotifyHierarchyChangedDown(parent, child, is_add, has_widget); +} + +void View::NotifyHierarchyChangedUp(View* parent, View* child, bool is_add) { + for (View* v = parent; v; v = v->parent()) { + if (is_add) + v->OnViewAdded(parent, child); + else + v->OnViewRemoved(parent, child); + } +} + +void View::NotifyHierarchyChangedDown(View* parent, View* child, bool is_add, + bool has_widget) { + ViewVector::iterator it = children_.begin(); + for (; it != children_.end(); ++it) { + CallViewNotification(*it, parent, child, is_add, has_widget); + (*it)->NotifyHierarchyChangedDown(parent, child, is_add, has_widget); + } +} + +void View::CallViewNotification(View* target, + View* parent, + View* child, + bool is_add, + bool has_widget) { + if (is_add) { + target->OnViewAdded(parent, child); + if (has_widget) + target->OnViewAddedToWidget(); + } else { + target->OnViewRemoved(parent, child); + if (has_widget) + target->OnViewRemovedFromWidget(); + } +} + +} // namespace ui diff --git a/ui/views/view.h b/ui/views/view.h new file mode 100644 index 0000000..bcb1451 --- /dev/null +++ b/ui/views/view.h @@ -0,0 +1,393 @@ +// 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 UI_VIEWS_VIEW_H_ +#define UI_VIEWS_VIEW_H_ + +#include <vector> + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "gfx/rect.h" +#include "ui/views/events/event.h" + +namespace gfx { +class Canvas; +class Point; +class Rect; +class Size; +} + +namespace ui { +namespace internal { +class RootView; +} +class Accelerator; +class Border; +class ContextMenuController; +class DragController; +class FocusManager; +class FocusTraversable; +class LayoutManager; +class ThemeProvider; +class Widget; + +//////////////////////////////////////////////////////////////////////////////// +// View class +// +// View encapsulates rendering, layout and event handling for rectangles within +// a view hierarchy. +// +// Client code typically subclasses View and overrides virtual methods to +// handle painting, child view positioning and handle certain types of events. +// +// Views are owned by their parent View unless specified otherwise. This means +// that in most cases Views are automatically destroyed when the window that +// contains them is destroyed. +// +// TODO(beng): consider the visibility of many of these methods. +// consider making RootView a friend and making many private or +// protected. +class View { + public: + typedef std::vector<View*> ViewVector; + + // Creation and lifetime ----------------------------------------------------- + View(); + virtual ~View(); + + // By default a View is owned by its parent unless specified otherwise here. + bool parent_owned() const { return parent_owned_; } + void set_parent_owned(bool parent_owned) { parent_owned_ = parent_owned; } + + void set_drag_controller(DragController* drag_controller) { + drag_controller_ = drag_controller; + } + + // Size and disposition ------------------------------------------------------ + + void SetBounds(int x, int y, int width, int height); + void SetBoundsRect(const gfx::Rect& bounds); + void SetSize(const gfx::Size& size); + void SetPosition(const gfx::Point& position); + gfx::Rect bounds() const { return bounds_; } + gfx::Rect GetVisibleBounds() const; + int x() const { return bounds_.x(); } + int y() const { return bounds_.y(); } + int width() const { return bounds_.width(); } + int height() const { return bounds_.height(); } + + // Owned by the view. + void SetBorder(Border* border); + Border* border() { return border_.get(); } + + // Returns the bounds of the content area of the view, i.e. the rectangle + // enclosed by the view's border. + gfx::Rect GetContentsBounds() const; + + // Override to be notified when the bounds of a view have changed. + virtual void OnBoundsChanged(); + + virtual gfx::Size GetPreferredSize() const; + virtual gfx::Size GetMinimumSize() const; + + void SetLayoutManager(LayoutManager* layout_manager); + virtual void Layout(); + + // If a View is not visible, it will not be rendered, focused, etc. + bool visible() const { return visible_; } + void SetVisible(bool visible); + + // Disabled Views will not receive mouse press/release events, nor can they be + // focused. + bool enabled() const { return enabled_; } + void SetEnabled(bool enabled); + + // Coordinate conversion ----------------------------------------------------- + + // Converts a point from the coordinate system of |source| to |target|. + static void ConvertPointToView(View* source, View* target, gfx::Point* point); + + // Converts a point from the coordinate system of |source| to the screen. + // If |source| is not attached to a Widget that is in screen space, |point| is + // not modified. + static void ConvertPointToScreen(View* source, gfx::Point* point); + + // Converts a point from the coordinate system of |source| to the Widget that + // most closely contains it. + static void ConvertPointToWidget(View* source, gfx::Point* point); + + // Tree operations ----------------------------------------------------------- + + // Returns the Widget that contains this View, or NULL if it is not contained + // within a Widget. + virtual Widget* GetWidget() const; + + // Adds a View as a child of this one, optionally at |index|. + void AddChildView(View* view); + void AddChildViewAt(View* view, size_t index); + + // Removes a View as a child of this View. Does not delete the child. + View* RemoveChildView(View* view); + + // Removes all View children of this View. Deletes the children if + // |delete_children| is true. + void RemoveAllChildViews(bool delete_children); + + // Returns the View at the specified |index|. + View* GetChildViewAt(size_t index); + + // Returns the number of child views. + size_t child_count() const { return children_.size(); } + + // Returns the parent View, or NULL if this View has no parent. + View* parent() const { return parent_; } + + // Returns true if |child| is contained within this View's hierarchy, even as + // an indirect descendant. Will return true if child is also this View. + bool Contains(View* child); + + // Returns the visible View that most closely contains the specified point. + // |point| is in this View's coordinates. + // This function is used by the event processing system in the Widget to + // locate views for event targeting. Override this function if you wish to + // specify a view other than the one most closely enclosing |point| to receive + // notifications for events within it. + // TODO(beng): This is [ab]used primarily for event handling. Should be + // renamed to something like GetViewForEvent(). + virtual View* GetViewForPoint(const gfx::Point& point) const; + + // Returns true if the specified point is contained within this View or its + // hit test mask. |point| is in this View's coordinates. + bool HitTest(const gfx::Point& point) const; + + int id() const { return id_; } + void set_id(int id) { id_ = id; } + int group() const { return group_; } + void set_group(int group) { group_ = group; } + + // Returns the View within this View's hierarchy whose id matches that + // specified. + View* GetViewById(int id) const; + + // Populates a ViewVector with the Views within this View's hierarchy that + // match the specified group id. + void GetViewsWithGroup(int group, ViewVector* vec) const; + + // Called on every view in the hierarchy when a view is added or removed. + virtual void OnViewAdded(View* parent, View* child); + virtual void OnViewRemoved(View* parent, View* child); + + // Called on a View when it is added or removed from a Widget. + virtual void OnViewAddedToWidget(); + virtual void OnViewRemovedFromWidget(); + + // Accelerators -------------------------------------------------------------- + + // Accelerator Registration. + void AddAccelerator(const Accelerator& accelerator); + void RemoveAccelerator(const Accelerator& accelerator); + void RemoveAllAccelerators(); + + virtual bool OnAcceleratorPressed(const Accelerator& accelerator); + + // Focus --------------------------------------------------------------------- + + // Manager. + FocusManager* GetFocusManager() const; + + // Traversal. + virtual FocusTraversable* GetFocusTraversable() const; + View* GetNextFocusableView() const; + View* GetPreviousFocusableView() const; + + // Attributes. + virtual bool SkipDefaultKeyEventProcessing(const KeyEvent& event) const; + void set_focusable(bool focusable) { focusable_ = focusable; } + bool IsFocusable() const; + + bool HasFocus() const; + void RequestFocus(); + + virtual void OnFocus(/* const FocusEvent& event */); + virtual void OnBlur(); + + // Input --------------------------------------------------------------------- + + virtual bool OnKeyPressed(const KeyEvent& event); + virtual bool OnKeyReleased(const KeyEvent& event); + virtual bool OnMouseWheel(const MouseWheelEvent& event); + // To receive OnMouseDragged() or OnMouseReleased() events, overriding classes + // must return true from this function. + virtual bool OnMousePressed(const MouseEvent& event); + virtual bool OnMouseDragged(const MouseEvent& event); + virtual void OnMouseReleased(const MouseEvent& event); + virtual void OnMouseCaptureLost(); + virtual void OnMouseMoved(const MouseEvent& event); + virtual void OnMouseEntered(const MouseEvent& event); + virtual void OnMouseExited(const MouseEvent& event); + + virtual gfx::NativeCursor GetCursorForPoint(const gfx::Point& point); + + // Painting ------------------------------------------------------------------ + + // Add all or part of a View's bounds to the enclosing Widget's invalid + // rectangle. This will result in those areas being re-painted on the next + // update. + void Invalidate(); + virtual void InvalidateRect(const gfx::Rect& invalid_rect); + + // Called by the framework to paint a View. Performs translation and clipping + // for View coordinates and language direction as required, allows the View + // to paint itself via the various OnPaint*() event handlers and then paints + // the hierarchy beneath it. + // TODO(beng): Make private? + void Paint(gfx::Canvas* canvas); + + // Responsible for calling Paint() on child Views. Override to control the + // order child Views are painted. + virtual void PaintChildren(gfx::Canvas* canvas); + + // Override to provide rendering in any part of the View's bounds. Typically + // this is the "contents" of the view. If you override this method you will + // have to call the subsequent OnPaint*() methods manually. + virtual void OnPaint(gfx::Canvas* canvas); + + // Override to paint a background before any content is drawn. Typically this + // is done if you are satisfied with a default OnPaint handler but wish to + // supply a different background. + virtual void OnPaintBackground(gfx::Canvas* canvas); + + // Override to paint a border not specified by SetBorder(). + virtual void OnPaintBorder(gfx::Canvas* canvas); + + // Override to paint a focus border (usually a dotted rectangle) around + // relevant contents. + virtual void OnPaintFocusBorder(gfx::Canvas* canvas); + + // Context menus ------------------------------------------------------------- + + void set_context_menu_controller( + ContextMenuController* context_menu_controller) { + context_menu_controller_ = context_menu_controller; + } + + // Resources ----------------------------------------------------------------- + + ThemeProvider* GetThemeProvider() const; + + private: + friend internal::RootView; + + // State collected during a MousePressed event to detect possible drag + // operations. + struct DragInfo { + // Sets possible_drag to false and start_x/y to 0. This is invoked by + // RootView prior to invoke MousePressed(). + void Reset(); + + // Sets possible_drag to true and start_pt to the specified point. + // This is invoked by the target view if it detects the press may generate + // a drag. + void PossibleDrag(const gfx::Point& point); + + // Whether the press may generate a drag. + bool possible_drag; + + // Position of the mouse press in screen coordinates. + gfx::Point press_point; + }; + + // Drag & Drop --------------------------------------------------------------- + int GetDragOperations(const gfx::Point& point); + void WriteDragData(const gfx::Point& point, OSExchangeData* data); + void StartShellDrag(const MouseEvent& event, const gfx::Point& press_point); + + // RootView API -------------------------------------------------------------- + // These methods are designed to be called by the RootView. The RootView + // should limit its interaction with the View to these methods and the public + // API. + bool MousePressed(const MouseEvent& event, DragInfo* drag_info); + bool MouseDragged(const MouseEvent& event, DragInfo* drag_info); + void MouseReleased(const MouseEvent& event); + + // Tree operations ----------------------------------------------------------- + void NotifyHierarchyChanged(View* parent, View* child, bool is_add); + void NotifyHierarchyChangedUp(View* parent, View* child, bool is_add); + void NotifyHierarchyChangedDown(View* parent, View* child, bool is_add, + bool has_widget); + void CallViewNotification(View* target, + View* parent, + View* child, + bool is_add, + bool has_widget); + + // The View's parent view. This is set and reset when the View is added and + // removed from a hierarchy. + View* parent_; + + // The View's children. + ViewVector children_; + + // True if the hierarchy (i.e. the parent View) is responsible for deleting + // this View. Default is true. + bool parent_owned_; + + // The bounds of the View, in its parent's coordinates. + gfx::Rect bounds_; + + scoped_ptr<Border> border_; + + // Whether or not this View is visible. The view still participates in layout + // but will not be painted. + bool visible_; + + // Whether or not this View is enabled. When disabled, the event system will + // not propagate un-handled events beyond the View in the hierarchy. + bool enabled_; + + // An identifier for this View. Caller must guarantee uniqueness. + int id_; + + // An identifier for a group of potentially related Views. + int group_; + + // True if this View is focusable by the FocusManager. + bool focusable_; + + // An optional helper that handles layout for child views. + scoped_ptr<LayoutManager> layout_manager_; + + // Shows the context menu. + ContextMenuController* context_menu_controller_; + + // Delegate for drag and drop related functionality. + DragController* drag_controller_; + + DISALLOW_COPY_AND_ASSIGN(View); +}; + +} // namespace ui + +#endif // UI_VIEWS_VIEW_H_ + +/* + +TODO(beng): +- focus +- accessibility +- scrolling +- cursors +- tooltips +- rtl +- l10n +- mousewheel +- more on painting +- layer stuff +- investigate why assorted notifications are necessary +- native_widget_views +- native_widget_gtk +- pick a name + +*/
\ No newline at end of file diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc new file mode 100644 index 0000000..14873a0 --- /dev/null +++ b/ui/views/view_unittest.cc @@ -0,0 +1,204 @@ +// 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 <algorithm> + +#include "gfx/canvas.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" + +namespace ui { + +class ViewTest : public testing::Test { + public: + ViewTest() {} + virtual ~ViewTest() {} + + private: + DISALLOW_COPY_AND_ASSIGN(ViewTest); +}; + +TEST_F(ViewTest, SizeAndDisposition) { + View v; + EXPECT_TRUE(v.bounds().IsEmpty()); + EXPECT_TRUE(v.visible()); + + v.SetBoundsRect(gfx::Rect(10, 10, 200, 200)); + EXPECT_EQ(200, v.width()); + + EXPECT_TRUE(v.GetPreferredSize().IsEmpty()); +} + +TEST_F(ViewTest, TreeOperations) { + View v; + EXPECT_EQ(NULL, v.GetWidget()); + EXPECT_EQ(0, v.child_count()); + + View child1; + v.AddChildView(&child1); + EXPECT_EQ(1, v.child_count()); + EXPECT_EQ(&v, child1.parent()); + + View child2; + v.AddChildViewAt(&child2, 0); + EXPECT_EQ(2, v.child_count()); + EXPECT_EQ(child1.parent(), child2.parent()); + + v.RemoveChildView(&child2); + EXPECT_EQ(1, v.child_count()); + EXPECT_EQ(NULL, child2.parent()); + + //v.RemoveAllChildViews(false); + //EXPECT_EQ(0, v.child_count()); +} + +class ObserverView : public View { + public: + ObserverView() + : view_added_(false), + view_removed_(false), + subject_view_(NULL) {} + virtual ~ObserverView() {} + + void ResetTestState() { + view_added_ = false; + view_removed_ = false; + subject_view_ = NULL; + } + + // Overridden from View: + virtual void OnViewAdded(View* parent, View* child) { + subject_view_ = child; + view_added_ = true; + view_removed_ = false; + } + virtual void OnViewRemoved(View* parent, View* child) { + subject_view_ = child; + view_removed_ = true; + view_added_ = false; + } + + bool view_added() const { return view_added_; } + bool view_removed() const { return view_removed_; } + View* subject_view() const { return subject_view_; } + + private: + bool view_added_; + bool view_removed_; + View* subject_view_; + + DISALLOW_COPY_AND_ASSIGN(ObserverView); +}; + +class WidgetObserverView : public View { + public: + WidgetObserverView() : in_widget_(false) {} + virtual ~WidgetObserverView() {} + + // Overridden from View: + virtual void OnViewAddedToWidget() { + in_widget_ = true; + } + virtual void OnViewRemovedFromWidget() { + in_widget_ = false; + } + + bool in_widget() const { return in_widget_; } + + private: + bool in_widget_; + + DISALLOW_COPY_AND_ASSIGN(WidgetObserverView); +}; + +/* +TEST_F(ViewTest, HierarchyObserver) { + ObserverView ov; + Widget widget(&ov); + widget.InitWithNativeViewParent(NULL, gfx::Rect(20, 20, 400, 400)); + + // |ov|'s addition to the RootView's hierarchy should have caused these values + // to be set. + EXPECT_TRUE(ov.view_added()); + EXPECT_FALSE(ov.view_removed()); + EXPECT_EQ(&ov, ov.subject_view()); + + ov.ResetTestState(); + + // Direct descendants. + View v2; + ov.AddChildView(&v2); + EXPECT_TRUE(ov.view_added()); + EXPECT_FALSE(ov.view_removed()); + EXPECT_EQ(&v2, ov.subject_view()); + + ov.ResetTestState(); + + // Nested Views and Widget addition. + WidgetObserverView v3; + EXPECT_FALSE(v3.in_widget()); + v2.AddChildView(&v3); + EXPECT_TRUE(v3.in_widget()); + EXPECT_EQ(&widget, v3.GetWidget()); + EXPECT_TRUE(ov.view_added()); + EXPECT_FALSE(ov.view_removed()); + EXPECT_EQ(&v3, ov.subject_view()); + + ov.ResetTestState(); + + // Removal and Widget removal. + ov.RemoveChildView(&v2); + EXPECT_FALSE(ov.view_added()); + EXPECT_TRUE(ov.view_removed()); + EXPECT_EQ(&v2, ov.subject_view()); + + EXPECT_FALSE(v3.in_widget()); + EXPECT_EQ(NULL, v3.GetWidget()); +} +*/ + +TEST_F(ViewTest, Ids) { + const int kV1Id = 1; + const int kV2Id = 2; + const int kV3Id = 3; + const int kV4Id = 4; + const int kV5Id = 5; + const int kGroupId = 1; + View v1; + v1.set_id(kV1Id); + View v2; + v2.set_id(kV2Id); + View v3; + v3.set_id(kV3Id); + v3.set_group(kGroupId); + View v4; + v4.set_id(kV4Id); + v4.set_group(kGroupId); + v1.AddChildView(&v2); + v2.AddChildView(&v3); + v2.AddChildView(&v4); + + EXPECT_EQ(&v4, v1.GetViewById(kV4Id)); + EXPECT_EQ(&v1, v1.GetViewById(kV1Id)); + EXPECT_EQ(NULL, v1.GetViewById(kV5Id)); // No V5 exists. + + View::ViewVector vec; + v1.GetViewsWithGroup(kGroupId, &vec); + EXPECT_EQ(2, vec.size()); + View::ViewVector::const_iterator it = find(vec.begin(), vec.end(), &v3); + EXPECT_NE(vec.end(), it); + it = find(vec.begin(), vec.end(), &v4); + EXPECT_NE(vec.end(), it); +} + +TEST_F(ViewTest, EventHandlers) { + +} + +TEST_F(ViewTest, Painting) { + +} + +} // namespace ui diff --git a/ui/views/views.gyp b/ui/views/views.gyp new file mode 100644 index 0000000..fa9ad5a --- /dev/null +++ b/ui/views/views.gyp @@ -0,0 +1,186 @@ +# Copyright (c) 2010 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'target_defaults': { + 'sources/': [ + ['exclude', '/(cocoa|gtk|win)/'], + ['exclude', '_(cocoa|gtk|linux|mac|posix|skia|win|x)\\.(cc|mm?)$'], + ['exclude', '/(gtk|win|x11)_[^/]*\\.cc$'], + ], + 'conditions': [ + ['OS=="linux" or OS=="freebsd" or OS=="openbsd"', {'sources/': [ + ['include', '/gtk/'], + ['include', '_(gtk|linux|posix|skia|x)\\.cc$'], + ['include', '/(gtk|x11)_[^/]*\\.cc$'], + ]}], + ['OS=="mac"', {'sources/': [ + ['include', '/cocoa/'], + ['include', '_(cocoa|mac|posix)\\.(cc|mm?)$'], + ]}, { # else: OS != "mac" + 'sources/': [ + ['exclude', '\\.mm?$'], + ], + }], + ['OS=="win"', {'sources/': [ + ['include', '_(win)\\.cc$'], + ['include', '/win/'], + ['include', '/win_[^/]*\\.cc$'], + ]}], + ['touchui==0', {'sources/': [ + ['exclude', 'event_x.cc$'], + ['exclude', 'native_menu_x.cc$'], + ['exclude', 'native_menu_x.h$'], + ['exclude', 'touchui/'], + ['exclude', '_(touch)\\.cc$'], + ]}], + ], + }, + 'targets': [ + { + 'target_name': 'v2', + 'type': '<(library)', + 'msvs_guid': '70760ECA-4D8B-47A4-ACDC-D3E7F25F0543', + 'dependencies': [ + '<(DEPTH)/app/app.gyp:app_base', + '<(DEPTH)/gfx/gfx.gyp:gfx', + '<(DEPTH)/skia/skia.gyp:skia', + ], + 'sources': [ + 'events/context_menu_controller.h', + 'events/drag_controller.h', + 'events/event.cc', + 'events/event.h', + 'events/event_win.cc', + 'layout/fill_layout.cc', + 'layout/fill_layout.h', + 'layout/layout_manager.cc', + 'layout/layout_manager.h', + 'native_types.h', + 'rendering/border.cc', + 'rendering/border.h', + 'view.cc', + 'view.h', + 'widget/native_widget.h', + 'widget/native_widget_listener.h', + 'widget/native_widget_views.cc', + 'widget/native_widget_views.h', + 'widget/native_widget_win.cc', + 'widget/native_widget_win.h', + 'widget/root_view.cc', + 'widget/root_view.h', + 'widget/widget.cc', + 'widget/widget.h', + 'window/window.cc', + 'window/window.h', + 'window/native_window.h', + 'window/native_window_views.cc', + 'window/native_window_views.h', + 'window/native_window_win.cc', + 'window/native_window_win.h', + ], + 'include_dirs': [ + '<(DEPTH)', + ], + 'conditions': [ + ['OS=="linux" or OS=="freebsd" or OS=="openbsd"', { + 'dependencies': [ + '<(DEPTH)/build/linux/system.gyp:gtk', + '<(DEPTH)/build/linux/system.gyp:x11', + '<(DEPTH)/build/linux/system.gyp:xext', + ], + 'sources!': [ + ], + }], + ['OS=="win"', { + 'include_dirs': [ + '<(DEPTH)/third_party/wtl/include', + ], + }], + ], + }, + { + 'target_name': 'views_unittests', + 'type': 'executable', + 'dependencies': [ + '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/base/base.gyp:test_support_base', + '<(DEPTH)/skia/skia.gyp:skia', + '<(DEPTH)/testing/gmock.gyp:gmock', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/third_party/icu/icu.gyp:icui18n', + '<(DEPTH)/third_party/icu/icu.gyp:icuuc', + 'v2', + ], + 'sources': [ + 'rendering/border_unittest.cc', + 'run_all_unittests.cc', + 'view_unittest.cc', + 'widget/widget_unittest.cc', + ], + 'include_dirs': [ + '<(DEPTH)', + ], + 'conditions': [ + ['OS=="linux" or OS=="freebsd" or OS=="openbsd"', { + 'dependencies': [ + '<(DEPTH)/build/linux/system.gyp:gtk', + '<(DEPTH)/chrome/chrome.gyp:packed_resources', + ], + 'conditions': [ + ['linux_use_tcmalloc==1', { + 'dependencies': [ + '<(DEPTH)/base/allocator/allocator.gyp:allocator', + ], + }], + ], + }, + ], + ['OS=="win"', { + 'sources': [ + 'widget/widget.rc', + 'widget/widget_resource.h', + ], + 'link_settings': { + 'libraries': [ + '-limm32.lib', + '-loleacc.lib', + ] + }, + }], + ], + }, + { + 'target_name': 'views_demo', + 'type': 'executable', + 'dependencies': [ + '<(DEPTH)/skia/skia.gyp:skia', + 'v2', + ], + 'sources': [ + 'demo/main.cc', + ], + 'include_dirs': [ + '<(DEPTH)', + ], + 'conditions': [ + ['OS=="win"', { + 'sources': [ + 'widget/widget.rc', + 'widget/widget_resource.h', + ], + }], + ], + }, + ], +} + +# Local Variables: +# tab-width:2 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/ui/views/widget/native_widget.h b/ui/views/widget/native_widget.h new file mode 100644 index 0000000..f8508a2 --- /dev/null +++ b/ui/views/widget/native_widget.h @@ -0,0 +1,69 @@ +// 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 UI_VIEWS_WIDGET_NATIVE_WIDGET_H_ +#define UI_VIEWS_WIDGET_NATIVE_WIDGET_H_ + +#include "ui/views/native_types.h" + +namespace gfx{ +class Path; +class Rect; +} + +namespace ui { +namespace internal { +class NativeWidgetListener; +} +class View; +class Widget; + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidget interface +// +// An interface implemented by an object that encapsulates rendering, event +// handling and widget management provided by an underlying native toolkit. +// +class NativeWidget { + public: + virtual ~NativeWidget() {} + + static NativeWidget* CreateNativeWidget( + internal::NativeWidgetListener* listener); + + // See Widget for documentation and notes. + virtual void InitWithNativeViewParent(gfx::NativeView parent, + const gfx::Rect& bounds) = 0; + virtual void InitWithWidgetParent(Widget* parent, + const gfx::Rect& bounds) = 0; + virtual void InitWithViewParent(View* parent, const gfx::Rect& bounds) = 0; + virtual void SetNativeWindowProperty(const char* name, void* value) = 0; + virtual void* GetNativeWindowProperty(const char* name) const = 0; + virtual gfx::Rect GetWindowScreenBounds() const = 0; + virtual gfx::Rect GetClientAreaScreenBounds() const = 0; + virtual void SetBounds(const gfx::Rect& bounds) = 0; + virtual void SetShape(const gfx::Path& shape) = 0; + virtual gfx::NativeView GetNativeView() const = 0; + virtual void Show() = 0; + virtual void Hide() = 0; + virtual void Close() = 0; + virtual void MoveAbove(NativeWidget* other) = 0; + virtual void SetAlwaysOnTop(bool always_on_top) = 0; + virtual bool IsVisible() const = 0; + virtual bool IsActive() const = 0; + + virtual void SetMouseCapture() = 0; + virtual void ReleaseMouseCapture() = 0; + virtual bool HasMouseCapture() const = 0; + virtual bool ShouldReleaseCaptureOnMouseReleased() const = 0; + + virtual void Invalidate() = 0; + virtual void InvalidateRect(const gfx::Rect& invalid_rect) = 0; + virtual void Paint() = 0; +}; + +} // namespace ui + +#endif // UI_VIEWS_WIDGET_NATIVE_WIDGET_H_ + diff --git a/ui/views/widget/native_widget_listener.h b/ui/views/widget/native_widget_listener.h new file mode 100644 index 0000000..959c14f --- /dev/null +++ b/ui/views/widget/native_widget_listener.h @@ -0,0 +1,53 @@ +// 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 UI_VIEWS_WIDGET_NATIVE_WIDGET_LISTENER_H_ +#define UI_VIEWS_WIDGET_NATIVE_WIDGET_LISTENER_H_ + +namespace gfx { +class Canvas; +class Point; +class Size; +} + +namespace ui { +class KeyEvent; +class MouseEvent; +class MouseWheelEvent; + +namespace internal { + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetListener interface +// +// An interface implemented by the Widget that handles events sent from a +// NativeWidget implementation. +// +class NativeWidgetListener { + public: + virtual ~NativeWidgetListener() {} + + virtual void OnClose() = 0; + + virtual void OnDestroy() = 0; + virtual void OnDisplayChanged() = 0; + + virtual bool OnKeyEvent(const KeyEvent& event) = 0; + + virtual void OnMouseCaptureLost() = 0; + + virtual bool OnMouseEvent(const MouseEvent& event) = 0; + virtual bool OnMouseWheelEvent(const MouseWheelEvent& event) = 0; + + virtual void OnNativeWidgetCreated() = 0; + + virtual void OnPaint(gfx::Canvas* canvas) = 0; + virtual void OnSizeChanged(const gfx::Size& size) = 0; + virtual void OnWorkAreaChanged() = 0; +}; + +} // namespace internal +} // namespace ui + +#endif // UI_VIEWS_WIDGET_NATIVE_WIDGET_LISTENER_H_ diff --git a/ui/views/widget/native_widget_views.cc b/ui/views/widget/native_widget_views.cc new file mode 100644 index 0000000..edb99fc --- /dev/null +++ b/ui/views/widget/native_widget_views.cc @@ -0,0 +1,10 @@ +// 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. + +namespace ui { +namespace internal { + +} // namespace internal +} // namespace ui + diff --git a/ui/views/widget/native_widget_views.h b/ui/views/widget/native_widget_views.h new file mode 100644 index 0000000..766da05 --- /dev/null +++ b/ui/views/widget/native_widget_views.h @@ -0,0 +1,15 @@ +// 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 UI_VIEWS_WIDGET_NATIVE_WIDGET_VIEWS_H_ +#define UI_VIEWS_WIDGET_NATIVE_WIDGET_VIEWS_H_ +#pragma once + +namespace ui { +namespace internal { + +} // namespace internal +} // namespace ui + +#endif // UI_VIEWS_WIDGET_NATIVE_WIDGET_VIEWS_H_ diff --git a/ui/views/widget/native_widget_win.cc b/ui/views/widget/native_widget_win.cc new file mode 100644 index 0000000..c5bd930 --- /dev/null +++ b/ui/views/widget/native_widget_win.cc @@ -0,0 +1,626 @@ +// 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 "ui/views/widget/native_widget_win.h" + +#include "base/scoped_ptr.h" +#include "gfx/canvas_skia.h" +#include "gfx/path.h" +#include "gfx/native_theme_win.h" +#include "ui/base/system_monitor/system_monitor.h" +#include "ui/base/view_prop.h" +#include "ui/views/view.h" +#include "ui/views/widget/native_widget_listener.h" +#include "ui/views/widget/widget.h" + +namespace ui { +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::InitWithWidgetParent(Widget* parent, + const gfx::Rect& bounds) { + InitWithNativeViewParent(parent->native_widget()->GetNativeView(), bounds); +} + +void NativeWidgetWin::InitWithViewParent(View* parent, + const gfx::Rect& bounds) { + InitWithWidgetParent(parent->GetWidget(), 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 ViewProp(hwnd(), name, value)); +} + +void* NativeWidgetWin::GetNativeWindowProperty(const char* name) const { + return 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); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// 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) { + 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) { + // TODO(beng): focus + 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) { + SystemMonitor* monitor = 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) { + // TODO(beng): focus + 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)); + } + // 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)); +} + +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); +} + +} // namespace ui diff --git a/ui/views/widget/native_widget_win.h b/ui/views/widget/native_widget_win.h new file mode 100644 index 0000000..0434a97 --- /dev/null +++ b/ui/views/widget/native_widget_win.h @@ -0,0 +1,271 @@ +// 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 UI_VIEWS_WIDGET_NATIVE_WIDGET_WIN_H_ +#define UI_VIEWS_WIDGET_NATIVE_WIDGET_WIN_H_ + +#include "ui/base/win/window_impl.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "base/scoped_vector.h" +#include "ui/views/widget/native_widget.h" + +namespace gfx { +class CanvasSkia; +} + +namespace ui { +class ViewProp; +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; + +class NativeWidgetListener; + +//////////////////////////////////////////////////////////////////////////////// +// 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<ViewProp> ViewProps; + + // Overridden from NativeWidget: + virtual void InitWithNativeViewParent(gfx::NativeView parent, + const gfx::Rect& bounds); + virtual void InitWithWidgetParent(Widget* parent, + const gfx::Rect& bounds); + virtual void InitWithViewParent(View* 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 void Invalidate(); + virtual void InvalidateRect(const gfx::Rect& invalid_rect); + virtual void Paint(); + virtual bool IsVisible() const; + virtual bool IsActive() const; + virtual void SetMouseCapture(); + virtual void ReleaseMouseCapture(); + virtual bool HasMouseCapture() const; + virtual bool ShouldReleaseCaptureOnMouseReleased() 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 Widget. + ViewProps props_; + + DISALLOW_COPY_AND_ASSIGN(NativeWidgetWin); +}; + +} // namespace internal +} // namespace ui + +#endif // UI_VIEWS_WIDGET_NATIVE_WIDGET_WIN_H_ + diff --git a/ui/views/widget/root_view.cc b/ui/views/widget/root_view.cc new file mode 100644 index 0000000..47371cd --- /dev/null +++ b/ui/views/widget/root_view.cc @@ -0,0 +1,145 @@ +// 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 "ui/views/widget/root_view.h" + +#include "ui/views/layout/fill_layout.h" +#include "ui/views/widget/widget.h" + +namespace ui { +namespace internal { + +//////////////////////////////////////////////////////////////////////////////// +// RootView, public: + +RootView::RootView(Widget* widget, View* contents_view) + : widget_(widget), + mouse_pressed_handler_(NULL), + mouse_move_handler_(NULL) { + SetLayoutManager(new FillLayout); + AddChildView(contents_view); +} + +RootView::~RootView() { +} + +//////////////////////////////////////////////////////////////////////////////// +// RootView, View overrides: + +void RootView::OnViewRemoved(View* parent, View* child) { + if (child == mouse_pressed_handler_) + mouse_pressed_handler_ = NULL; +} + +bool RootView::OnKeyPressed(const KeyEvent& event) { + return true; +} + +bool RootView::OnKeyReleased(const KeyEvent& event) { + return true; +} + +bool RootView::OnMouseWheel(const MouseWheelEvent& event) { + return true; +} + +bool RootView::OnMousePressed(const MouseEvent& event) { + bool handled = false; + + // Find the most View most tightly enclosing the event location that wants to + // handle events. + mouse_pressed_handler_ = GetViewForPoint(event.location()); + + // Walk up the tree from that View until we find one that handles it. + while (mouse_pressed_handler_ && mouse_pressed_handler_ != this) { + if (!mouse_pressed_handler_->enabled()) + break; + + MouseEvent target_event(event, this, mouse_pressed_handler_); + drag_info_.Reset(); + bool handled = mouse_pressed_handler_->MousePressed(target_event, + &drag_info_); + // MousePressed() may have resulted in the handler removing itself from the + // hierarchy, which will NULL-out |mouse_pressed_handler_|. + if (!mouse_pressed_handler_) + break; + + if (handled) + return true; + + mouse_pressed_handler_ = mouse_pressed_handler_->parent(); + } + return false; +} + +bool RootView::OnMouseDragged(const MouseEvent& event) { + // TODO(beng): Update cursor. + if (mouse_pressed_handler_) + return mouse_pressed_handler_->MouseDragged(event, &drag_info_); + return false; +} + +void RootView::OnMouseReleased(const MouseEvent& event) { + // TODO(beng): Update cursor. + if (mouse_pressed_handler_) { + MouseEvent released_event(event, this, mouse_pressed_handler_); + View* mouse_pressed_handler = mouse_pressed_handler_; + mouse_pressed_handler_ = NULL; + mouse_pressed_handler->MouseReleased(released_event); + } +} + +void RootView::OnMouseCaptureLost() { + if (mouse_pressed_handler_) { + View* mouse_pressed_handler = mouse_pressed_handler_; + mouse_pressed_handler_ = NULL; + mouse_pressed_handler->OnMouseCaptureLost(); + } +} + +void RootView::OnMouseMoved(const MouseEvent& event) { + // TODO(beng): Update cursor. + View* v = GetViewForPoint(event.location()); + while (v && !v->enabled() && (v != mouse_move_handler_)) + v = v->parent(); + if (v && v != this) { + if (v != mouse_move_handler_) { + OnMouseExited(event); + mouse_move_handler_ = v; + MouseEvent entered_event(event, this, mouse_move_handler_); + mouse_move_handler_->OnMouseEntered(entered_event); + } + MouseEvent moved_event(event, this, mouse_move_handler_); + mouse_move_handler_->OnMouseMoved(moved_event); + } else { + OnMouseExited(event); + } +} + +void RootView::OnMouseExited(const MouseEvent& event) { + if (mouse_move_handler_) { + MouseEvent exited_event(event, this, mouse_move_handler_); + mouse_move_handler_->OnMouseExited(exited_event); + mouse_move_handler_ = NULL; + } +} + +void RootView::Paint(gfx::Canvas* canvas) { + // Public pass-thru to protected base class method. + View::Paint(canvas); +} + +void RootView::InvalidateRect(const gfx::Rect& invalid_rect) { + widget_->InvalidateRect(invalid_rect); +} + +Widget* RootView::GetWidget() const { + return widget_; +} + +//////////////////////////////////////////////////////////////////////////////// +// RootView, private: + +} // namespace internal +} // namespace ui diff --git a/ui/views/widget/root_view.h b/ui/views/widget/root_view.h new file mode 100644 index 0000000..308717c --- /dev/null +++ b/ui/views/widget/root_view.h @@ -0,0 +1,62 @@ +// 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 UI_VIEWS_WIDGET_ROOT_VIEW_H_ +#define UI_VIEWS_WIDGET_ROOT_VIEW_H_ +#pragma once + +#include "ui/views/view.h" + +namespace ui { +namespace internal { + +//////////////////////////////////////////////////////////////////////////////// +// RootView class +// +// A View subclass that owns a View hierarchy. Used by the Widget to perform +// View-specific event tracking. +// +class RootView : public View { + public: + RootView(Widget* widget, View* contents_view); + virtual ~RootView(); + + // Overridden from View: + virtual void OnViewRemoved(View* parent, View* child); + virtual bool OnKeyPressed(const KeyEvent& event); + virtual bool OnKeyReleased(const KeyEvent& event); + virtual bool OnMouseWheel(const MouseWheelEvent& event); + virtual bool OnMousePressed(const MouseEvent& event); + virtual bool OnMouseDragged(const MouseEvent& event); + virtual void OnMouseReleased(const MouseEvent& event); + virtual void OnMouseCaptureLost(); + virtual void OnMouseMoved(const MouseEvent& event); + virtual void OnMouseExited(const MouseEvent& event); + virtual void Paint(gfx::Canvas* canvas); + virtual void InvalidateRect(const gfx::Rect& invalid_rect); + virtual Widget* GetWidget() const; + + private: + Widget* widget_; + + // The View that the mouse was pressed down on. Used to track drag operations + // and to target mouse-released events at the correct view. + View* mouse_pressed_handler_; + + // The View that the mouse is currently over. Used to track mouse-exited + // events as the mouse moves from view to view, and when the mouse leaves the + // bounds of the containing Widget. + View* mouse_move_handler_; + + // State captured on mouse press that would be useful for a potential drag + // operation. + DragInfo drag_info_; + + DISALLOW_COPY_AND_ASSIGN(RootView); +}; + +} // namespace internal +} // namespace ui + +#endif // UI_VIEWS_WIDGET_ROOT_VIEW_H_ diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc new file mode 100644 index 0000000..36af0b1 --- /dev/null +++ b/ui/views/widget/widget.cc @@ -0,0 +1,219 @@ +// 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 "ui/views/widget/widget.h" + +#include "base/compiler_specific.h" +#include "base/message_loop.h" +#include "ui/views/view.h" +#include "ui/views/widget/native_widget.h" +#include "ui/views/widget/root_view.h" + +namespace ui { + +namespace { + +// TODO(beng): move to platform file +int GetHorizontalDragThreshold() { + static int threshold = -1; + if (threshold == -1) + threshold = GetSystemMetrics(SM_CXDRAG) / 2; + return threshold; +} + +// TODO(beng): move to platform file +int GetVerticalDragThreshold() { + static int threshold = -1; + if (threshold == -1) + threshold = GetSystemMetrics(SM_CYDRAG) / 2; + return threshold; +} + +bool ExceededDragThreshold(int delta_x, int delta_y) { + return (abs(delta_x) > GetHorizontalDragThreshold() || + abs(delta_y) > GetVerticalDragThreshold()); +} + +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget, public: + +Widget::Widget(View* contents_view) + : ALLOW_THIS_IN_INITIALIZER_LIST( + native_widget_(NativeWidget::CreateNativeWidget(this))), + ALLOW_THIS_IN_INITIALIZER_LIST( + root_view_(new internal::RootView(this, contents_view))), + is_mouse_button_pressed_(false), + last_mouse_event_was_move_(false), + ALLOW_THIS_IN_INITIALIZER_LIST(close_widget_factory_(this)) { +} + +Widget::~Widget() { +} + +void Widget::InitWithNativeViewParent(gfx::NativeView parent, + const gfx::Rect& bounds) { + native_widget_->InitWithNativeViewParent(parent, bounds); +} + +void Widget::InitWithWidgetParent(Widget* parent, const gfx::Rect& bounds) { + native_widget_->InitWithWidgetParent(parent, bounds); +} + +void Widget::InitWithViewParent(View* parent, const gfx::Rect& bounds) { + native_widget_->InitWithViewParent(parent, bounds); +} + +gfx::Rect Widget::GetWindowScreenBounds() const { + return native_widget_->GetWindowScreenBounds(); +} + +gfx::Rect Widget::GetClientAreaScreenBounds() const { + return native_widget_->GetClientAreaScreenBounds(); +} + +void Widget::SetBounds(const gfx::Rect& bounds) { + native_widget_->SetBounds(bounds); +} + +void Widget::SetShape(const gfx::Path& shape) { + native_widget_->SetShape(shape); +} + +void Widget::Show() { + native_widget_->Show(); +} + +void Widget::Hide() { + native_widget_->Hide(); +} + +void Widget::Close() { + native_widget_->Hide(); + + if (close_widget_factory_.empty()) { + MessageLoop::current()->PostTask(FROM_HERE, + close_widget_factory_.NewRunnableMethod(&Widget::CloseNow)); + } +} + +void Widget::MoveAbove(Widget* other) { + native_widget_->MoveAbove(other->native_widget()); +} + +void Widget::InvalidateRect(const gfx::Rect& invalid_rect) { + native_widget_->InvalidateRect(invalid_rect); +} + +ThemeProvider* Widget::GetThemeProvider() const { + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget, NativeWidgetListener implementation: + +void Widget::OnClose() { + Close(); +} + +void Widget::OnDestroy() { + if (delete_on_destroy_) + delete this; +} + +void Widget::OnDisplayChanged() { + // TODO(beng): +} + +bool Widget::OnKeyEvent(const KeyEvent& event) { + // find root view. + + //return root_view_->OnKeyEvent(event); + return true; +} + +void Widget::OnMouseCaptureLost() { + if (native_widget_->HasMouseCapture()) { + if (is_mouse_button_pressed_) + root_view_->OnMouseCaptureLost(); + is_mouse_button_pressed_ = false; + } +} + +bool Widget::OnMouseEvent(const MouseEvent& event) { + last_mouse_event_was_move_ = false; + switch (event.type()) { + case Event::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 Event::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); + return true; + case Event::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 Event::ET_MOUSE_EXITED: + root_view_->OnMouseExited(event); + return true; + } + return true; +} + +bool Widget::OnMouseWheelEvent(const MouseWheelEvent& event) { + return root_view_->OnMouseWheel(event); +} + +void Widget::OnNativeWidgetCreated() { +} + +void Widget::OnPaint(gfx::Canvas* canvas) { + root_view_->Paint(canvas); +} + +void Widget::OnSizeChanged(const gfx::Size& size) { + root_view_->SetSize(size); +} + +void Widget::OnWorkAreaChanged() { + +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget, private: + +void Widget::CloseNow() { + native_widget_->Close(); +} + +} // namespace ui + diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h new file mode 100644 index 0000000..9a68eab --- /dev/null +++ b/ui/views/widget/widget.h @@ -0,0 +1,131 @@ +// 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 UI_VIEWS_WIDGET_WIDGET_H_ +#define UI_VIEWS_WIDGET_WIDGET_H_ + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "gfx/point.h" +#include "ui/views/native_types.h" +#include "ui/views/widget/native_widget_listener.h" + +namespace gfx { +class Path; +class Rect; +} + +namespace ui { +namespace internal { +class RootView; +} +class NativeWidget; +class ThemeProvider; +class View; + +//////////////////////////////////////////////////////////////////////////////// +// Widget class +// +// Encapsulates the platform-specific rendering, event receiving and widget +// management aspects of the UI framework. +// +// Owns a RootView and thus a View hierarchy. Can contain child Widgets. +// Widget is a platform-independent type that communicates with a platform or +// context specific NativeWidget implementation. +// +// TODO(beng): Consider ownership of this object vs. NativeWidget. +class Widget : public internal::NativeWidgetListener { + public: + explicit Widget(View* contents_view); + virtual ~Widget(); + + bool set_delete_on_destroy(bool delete_on_destroy) { + delete_on_destroy_ = delete_on_destroy; + } + + // Initialization. + void InitWithNativeViewParent(gfx::NativeView parent, + const gfx::Rect& bounds); + void InitWithWidgetParent(Widget* parent, const gfx::Rect& bounds); + void InitWithViewParent(View* parent, const gfx::Rect& bounds); + + // 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(Widget* other); + void SetAlwaysOnTop(bool always_on_top); + + // Causes the specified rectangle to be added to the invalid rectangle for the + // Widget. + void InvalidateRect(const gfx::Rect& invalid_rect); + + // Returns a ThemeProvider that can be used to provide resources when + // rendering Views associated with this Widget. + ThemeProvider* GetThemeProvider() const; + + NativeWidget* native_widget() const { return native_widget_.get(); } + + private: + // NativeWidgetListener implementation: + 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 OnWorkAreaChanged(); + + // 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_; + + // A RootView that owns the View hierarchy within this Widget. + scoped_ptr<internal::RootView> root_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<Widget> close_widget_factory_; + + // True if the Widget should be automatically deleted when it is destroyed. + bool delete_on_destroy_; + + DISALLOW_COPY_AND_ASSIGN(Widget); +}; + +} // namespace ui + +#endif // UI_VIEWS_WIDGET_WIDGET_H_ + diff --git a/ui/views/widget/widget.rc b/ui/views/widget/widget.rc new file mode 100644 index 0000000..c2bfa98 --- /dev/null +++ b/ui/views/widget/widget.rc @@ -0,0 +1,9 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "ui/views/widget/widget_resource.h" +#include <windows.h> diff --git a/ui/views/widget/widget_resource.h b/ui/views/widget/widget_resource.h new file mode 100644 index 0000000..aaf3ebc --- /dev/null +++ b/ui/views/widget/widget_resource.h @@ -0,0 +1,11 @@ +// 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 UI_VIEWS_WIDGET_WIDGET_RESOURCE_H_ +#define UI_VIEWS_WIDGET_WIDGET_RESOURCE_H_ +#pragma once + +#define IDR_PIXEL_SHADER 100 + +#endif // UI_VIEWS_WIDGET_WIDGET_RESOURCE_H_ diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc new file mode 100644 index 0000000..2233e5d --- /dev/null +++ b/ui/views/widget/widget_unittest.cc @@ -0,0 +1,23 @@ +// 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 "testing/gtest/include/gtest/gtest.h" + +namespace ui { + +class WidgetTest : public testing::Test { + public: + WidgetTest() {} + virtual ~WidgetTest() {} + + private: + DISALLOW_COPY_AND_ASSIGN(WidgetTest); +}; + +TEST_F(WidgetTest, Init) { +} + +} // namespace ui + diff --git a/ui/views/window/native_window.h b/ui/views/window/native_window.h new file mode 100644 index 0000000..7a37f38 --- /dev/null +++ b/ui/views/window/native_window.h @@ -0,0 +1,3 @@ +// 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. diff --git a/ui/views/window/native_window_views.cc b/ui/views/window/native_window_views.cc new file mode 100644 index 0000000..7a37f38 --- /dev/null +++ b/ui/views/window/native_window_views.cc @@ -0,0 +1,3 @@ +// 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. diff --git a/ui/views/window/native_window_views.h b/ui/views/window/native_window_views.h new file mode 100644 index 0000000..7a37f38 --- /dev/null +++ b/ui/views/window/native_window_views.h @@ -0,0 +1,3 @@ +// 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. diff --git a/ui/views/window/native_window_win.cc b/ui/views/window/native_window_win.cc new file mode 100644 index 0000000..7a37f38 --- /dev/null +++ b/ui/views/window/native_window_win.cc @@ -0,0 +1,3 @@ +// 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. diff --git a/ui/views/window/native_window_win.h b/ui/views/window/native_window_win.h new file mode 100644 index 0000000..7a37f38 --- /dev/null +++ b/ui/views/window/native_window_win.h @@ -0,0 +1,3 @@ +// 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. diff --git a/ui/views/window/window.cc b/ui/views/window/window.cc new file mode 100644 index 0000000..7a37f38 --- /dev/null +++ b/ui/views/window/window.cc @@ -0,0 +1,3 @@ +// 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. diff --git a/ui/views/window/window.h b/ui/views/window/window.h new file mode 100644 index 0000000..7a37f38 --- /dev/null +++ b/ui/views/window/window.h @@ -0,0 +1,3 @@ +// 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. |