| Index: views/widget/native_widget_win.cc
|
| diff --git a/views/widget/native_widget_win.cc b/views/widget/native_widget_win.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..7c013f97545ac7f0eadb56d6cf83d19bd0da9139
|
| --- /dev/null
|
| +++ b/views/widget/native_widget_win.cc
|
| @@ -0,0 +1,2524 @@
|
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "views/widget/native_widget_win.h"
|
| +
|
| +#include <dwmapi.h>
|
| +#include <shellapi.h>
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/string_util.h"
|
| +#include "base/system_monitor/system_monitor.h"
|
| +#include "base/win/scoped_gdi_object.h"
|
| +#include "base/win/win_util.h"
|
| +#include "base/win/windows_version.h"
|
| +#include "ui/base/dragdrop/drag_drop_types.h"
|
| +#include "ui/base/dragdrop/drag_source.h"
|
| +#include "ui/base/dragdrop/os_exchange_data.h"
|
| +#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
|
| +#include "ui/base/keycodes/keyboard_code_conversion_win.h"
|
| +#include "ui/base/l10n/l10n_util_win.h"
|
| +#include "ui/base/theme_provider.h"
|
| +#include "ui/base/view_prop.h"
|
| +#include "ui/base/win/hwnd_util.h"
|
| +#include "ui/base/win/mouse_wheel_util.h"
|
| +#include "ui/gfx/canvas_skia.h"
|
| +#include "ui/gfx/canvas_skia_paint.h"
|
| +#include "ui/gfx/compositor/compositor.h"
|
| +#include "ui/gfx/icon_util.h"
|
| +#include "ui/gfx/native_theme_win.h"
|
| +#include "ui/gfx/path.h"
|
| +#include "ui/gfx/screen.h"
|
| +#include "ui/views/accessibility/native_view_accessibility_win.h"
|
| +#include "ui/views/focus/accelerator_handler.h"
|
| +#include "ui/views/focus/view_storage.h"
|
| +#include "ui/views/ime/input_method_win.h"
|
| +#include "ui/views/window/native_frame_view.h"
|
| +#include "views/controls/native_control_win.h"
|
| +#include "views/controls/textfield/native_textfield_views.h"
|
| +#include "views/views_delegate.h"
|
| +#include "views/widget/aero_tooltip_manager.h"
|
| +#include "views/widget/child_window_message_processor.h"
|
| +#include "views/widget/drop_target_win.h"
|
| +#include "views/widget/monitor_win.h"
|
| +#include "views/widget/native_widget_delegate.h"
|
| +#include "views/widget/root_view.h"
|
| +#include "views/widget/widget_delegate.h"
|
| +
|
| +#pragma comment(lib, "dwmapi.lib")
|
| +
|
| +using ui::ViewProp;
|
| +
|
| +namespace views {
|
| +
|
| +namespace {
|
| +
|
| +// Get the source HWND of the specified message. Depending on the message, the
|
| +// source HWND is encoded in either the WPARAM or the LPARAM value.
|
| +HWND GetControlHWNDForMessage(UINT message, WPARAM w_param, LPARAM l_param) {
|
| + // Each of the following messages can be sent by a child HWND and must be
|
| + // forwarded to its associated NativeControlWin for handling.
|
| + switch (message) {
|
| + case WM_NOTIFY:
|
| + return reinterpret_cast<NMHDR*>(l_param)->hwndFrom;
|
| + case WM_COMMAND:
|
| + return reinterpret_cast<HWND>(l_param);
|
| + case WM_CONTEXTMENU:
|
| + return reinterpret_cast<HWND>(w_param);
|
| + case WM_CTLCOLORBTN:
|
| + case WM_CTLCOLORSTATIC:
|
| + return reinterpret_cast<HWND>(l_param);
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +// Some messages may be sent to us by a child HWND. If this is the case, this
|
| +// function will forward those messages on to the object associated with the
|
| +// source HWND and return true, in which case the window procedure must not do
|
| +// any further processing of the message. If there is no associated
|
| +// ChildWindowMessageProcessor, the return value will be false and the WndProc
|
| +// can continue processing the message normally. |l_result| contains the result
|
| +// of the message processing by the control and must be returned by the WndProc
|
| +// if the return value is true.
|
| +bool ProcessChildWindowMessage(UINT message,
|
| + WPARAM w_param,
|
| + LPARAM l_param,
|
| + LRESULT* l_result) {
|
| + *l_result = 0;
|
| +
|
| + HWND control_hwnd = GetControlHWNDForMessage(message, w_param, l_param);
|
| + if (IsWindow(control_hwnd)) {
|
| + ChildWindowMessageProcessor* processor =
|
| + ChildWindowMessageProcessor::Get(control_hwnd);
|
| + if (processor)
|
| + return processor->ProcessMessage(message, w_param, l_param, l_result);
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +// Enumeration callback for NativeWidget::GetAllChildWidgets(). Called for each
|
| +// child HWND beneath the original HWND.
|
| +BOOL CALLBACK EnumerateChildWindowsForNativeWidgets(HWND hwnd, LPARAM l_param) {
|
| + Widget* widget = Widget::GetWidgetForNativeView(hwnd);
|
| + if (widget) {
|
| + Widget::Widgets* widgets = reinterpret_cast<Widget::Widgets*>(l_param);
|
| + widgets->insert(widget);
|
| + }
|
| + return TRUE;
|
| +}
|
| +
|
| +// Returns true if the WINDOWPOS data provided indicates the client area of
|
| +// the window may have changed size. This can be caused by the window being
|
| +// resized or its frame changing.
|
| +bool DidClientAreaSizeChange(const WINDOWPOS* window_pos) {
|
| + return !(window_pos->flags & SWP_NOSIZE) ||
|
| + window_pos->flags & SWP_FRAMECHANGED;
|
| +}
|
| +
|
| +// Callback used to notify child windows that the top level window received a
|
| +// DWMCompositionChanged message.
|
| +BOOL CALLBACK SendDwmCompositionChanged(HWND window, LPARAM param) {
|
| + SendMessage(window, WM_DWMCOMPOSITIONCHANGED, 0, 0);
|
| + return TRUE;
|
| +}
|
| +
|
| +// Tells the window its frame (non-client area) has changed.
|
| +void SendFrameChanged(HWND window) {
|
| + SetWindowPos(window, NULL, 0, 0, 0, 0,
|
| + SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOCOPYBITS |
|
| + SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREPOSITION |
|
| + SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER);
|
| +}
|
| +
|
| +// Enables or disables the menu item for the specified command and menu.
|
| +void EnableMenuItem(HMENU menu, UINT command, bool enabled) {
|
| + UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
|
| + EnableMenuItem(menu, command, flags);
|
| +}
|
| +
|
| +BOOL CALLBACK EnumChildWindowsForRedraw(HWND hwnd, LPARAM lparam) {
|
| + DWORD process_id;
|
| + GetWindowThreadProcessId(hwnd, &process_id);
|
| + int flags = RDW_INVALIDATE | RDW_NOCHILDREN | RDW_FRAME;
|
| + if (process_id == GetCurrentProcessId())
|
| + flags |= RDW_UPDATENOW;
|
| + RedrawWindow(hwnd, NULL, NULL, flags);
|
| + return TRUE;
|
| +}
|
| +
|
| +// See comments in OnNCPaint() for details of this struct.
|
| +struct ClipState {
|
| + // The window being painted.
|
| + HWND parent;
|
| +
|
| + // DC painting to.
|
| + HDC dc;
|
| +
|
| + // Origin of the window in terms of the screen.
|
| + int x;
|
| + int y;
|
| +};
|
| +
|
| +// See comments in OnNCPaint() for details of this function.
|
| +static BOOL CALLBACK ClipDCToChild(HWND window, LPARAM param) {
|
| + ClipState* clip_state = reinterpret_cast<ClipState*>(param);
|
| + if (GetParent(window) == clip_state->parent && IsWindowVisible(window)) {
|
| + RECT bounds;
|
| + GetWindowRect(window, &bounds);
|
| + ExcludeClipRect(clip_state->dc,
|
| + bounds.left - clip_state->x,
|
| + bounds.top - clip_state->y,
|
| + bounds.right - clip_state->x,
|
| + bounds.bottom - clip_state->y);
|
| + }
|
| + return TRUE;
|
| +}
|
| +
|
| +// The thickness of an auto-hide taskbar in pixels.
|
| +static const int kAutoHideTaskbarThicknessPx = 2;
|
| +
|
| +bool GetMonitorAndRects(const RECT& rect,
|
| + HMONITOR* monitor,
|
| + gfx::Rect* monitor_rect,
|
| + gfx::Rect* work_area) {
|
| + DCHECK(monitor);
|
| + DCHECK(monitor_rect);
|
| + DCHECK(work_area);
|
| + *monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL);
|
| + if (!*monitor)
|
| + return false;
|
| + MONITORINFO monitor_info = { 0 };
|
| + monitor_info.cbSize = sizeof(monitor_info);
|
| + GetMonitorInfo(*monitor, &monitor_info);
|
| + *monitor_rect = monitor_info.rcMonitor;
|
| + *work_area = monitor_info.rcWork;
|
| + return true;
|
| +}
|
| +
|
| +// Links the HWND to its NativeWidget.
|
| +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 kCustomObjectID = 1;
|
| +
|
| +const int kDragFrameWindowAlpha = 200;
|
| +
|
| +} // namespace
|
| +
|
| +// static
|
| +bool NativeWidgetWin::screen_reader_active_ = false;
|
| +
|
| +// A scoping class that prevents a window from being able to redraw in response
|
| +// to invalidations that may occur within it for the lifetime of the object.
|
| +//
|
| +// Why would we want such a thing? Well, it turns out Windows has some
|
| +// "unorthodox" behavior when it comes to painting its non-client areas.
|
| +// Occasionally, Windows will paint portions of the default non-client area
|
| +// right over the top of the custom frame. This is not simply fixed by handling
|
| +// WM_NCPAINT/WM_PAINT, with some investigation it turns out that this
|
| +// rendering is being done *inside* the default implementation of some message
|
| +// handlers and functions:
|
| +// . WM_SETTEXT
|
| +// . WM_SETICON
|
| +// . WM_NCLBUTTONDOWN
|
| +// . EnableMenuItem, called from our WM_INITMENU handler
|
| +// The solution is to handle these messages and call DefWindowProc ourselves,
|
| +// but prevent the window from being able to update itself for the duration of
|
| +// the call. We do this with this class, which automatically calls its
|
| +// associated Window's lock and unlock functions as it is created and destroyed.
|
| +// See documentation in those methods for the technique used.
|
| +//
|
| +// The lock only has an effect if the window was visible upon lock creation, as
|
| +// it doesn't guard against direct visiblility changes, and multiple locks may
|
| +// exist simultaneously to handle certain nested Windows messages.
|
| +//
|
| +// IMPORTANT: Do not use this scoping object for large scopes or periods of
|
| +// time! IT WILL PREVENT THE WINDOW FROM BEING REDRAWN! (duh).
|
| +//
|
| +// I would love to hear Raymond Chen's explanation for all this. And maybe a
|
| +// list of other messages that this applies to ;-)
|
| +class NativeWidgetWin::ScopedRedrawLock {
|
| + public:
|
| + explicit ScopedRedrawLock(NativeWidgetWin* widget)
|
| + : widget_(widget),
|
| + native_view_(widget_->GetNativeView()),
|
| + was_visible_(widget_->IsVisible()),
|
| + cancel_unlock_(false) {
|
| + if (was_visible_ && ::IsWindow(native_view_))
|
| + widget_->LockUpdates();
|
| + }
|
| +
|
| + ~ScopedRedrawLock() {
|
| + if (!cancel_unlock_ && was_visible_ && ::IsWindow(native_view_))
|
| + widget_->UnlockUpdates();
|
| + }
|
| +
|
| + // Cancel the unlock operation, call this if the Widget is being destroyed.
|
| + void CancelUnlockOperation() { cancel_unlock_ = true; }
|
| +
|
| + private:
|
| + // The widget having its style changed.
|
| + NativeWidgetWin* widget_;
|
| + // The widget's native view, cached to avoid action after window destruction.
|
| + gfx::NativeView native_view_;
|
| + // Records the widget visibility at the time of creation.
|
| + bool was_visible_;
|
| + // A flag indicating that the unlock operation was canceled.
|
| + bool cancel_unlock_;
|
| +};
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// NativeWidgetWin, public:
|
| +
|
| +NativeWidgetWin::NativeWidgetWin(internal::NativeWidgetDelegate* delegate)
|
| + : delegate_(delegate),
|
| + ALLOW_THIS_IN_INITIALIZER_LIST(close_widget_factory_(this)),
|
| + active_mouse_tracking_flags_(0),
|
| + use_layered_buffer_(false),
|
| + layered_alpha_(255),
|
| + ALLOW_THIS_IN_INITIALIZER_LIST(paint_layered_window_factory_(this)),
|
| + ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET),
|
| + can_update_layered_window_(true),
|
| + restore_focus_when_enabled_(false),
|
| + accessibility_view_events_index_(-1),
|
| + accessibility_view_events_(kMaxAccessibilityViewEvents),
|
| + previous_cursor_(NULL),
|
| + fullscreen_(false),
|
| + force_hidden_count_(0),
|
| + lock_updates_count_(0),
|
| + saved_window_style_(0),
|
| + ignore_window_pos_changes_(false),
|
| + ALLOW_THIS_IN_INITIALIZER_LIST(ignore_pos_changes_factory_(this)),
|
| + last_monitor_(NULL),
|
| + is_right_mouse_pressed_on_caption_(false),
|
| + restored_enabled_(false),
|
| + destroyed_(NULL),
|
| + has_non_client_view_(false) {
|
| +}
|
| +
|
| +NativeWidgetWin::~NativeWidgetWin() {
|
| + if (destroyed_ != NULL)
|
| + *destroyed_ = true;
|
| +
|
| + if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
|
| + delete delegate_;
|
| + else
|
| + CloseNow();
|
| +}
|
| +
|
| +// static
|
| +bool NativeWidgetWin::IsAeroGlassEnabled() {
|
| + if (base::win::GetVersion() < base::win::VERSION_VISTA)
|
| + return false;
|
| + // If composition is not enabled, we behave like on XP.
|
| + BOOL enabled = FALSE;
|
| + return SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled;
|
| +}
|
| +
|
| +// static
|
| +gfx::Font NativeWidgetWin::GetWindowTitleFont() {
|
| + NONCLIENTMETRICS ncm;
|
| + base::win::GetNonClientMetrics(&ncm);
|
| + l10n_util::AdjustUIFont(&(ncm.lfCaptionFont));
|
| + base::win::ScopedHFONT caption_font(CreateFontIndirect(&(ncm.lfCaptionFont)));
|
| + return gfx::Font(caption_font);
|
| +}
|
| +
|
| +void NativeWidgetWin::Show(int show_state) {
|
| + ShowWindow(show_state);
|
| + // When launched from certain programs like bash and Windows Live Messenger,
|
| + // show_state is set to SW_HIDE, so we need to correct that condition. We
|
| + // don't just change show_state to SW_SHOWNORMAL because MSDN says we must
|
| + // always first call ShowWindow with the specified value from STARTUPINFO,
|
| + // otherwise all future ShowWindow calls will be ignored (!!#@@#!). Instead,
|
| + // we call ShowWindow again in this case.
|
| + if (show_state == SW_HIDE) {
|
| + show_state = SW_SHOWNORMAL;
|
| + ShowWindow(show_state);
|
| + }
|
| +
|
| + // We need to explicitly activate the window if we've been shown with a state
|
| + // that should activate, because if we're opened from a desktop shortcut while
|
| + // an existing window is already running it doesn't seem to be enough to use
|
| + // one of these flags to activate the window.
|
| + if (show_state == SW_SHOWNORMAL || show_state == SW_SHOWMAXIMIZED)
|
| + Activate();
|
| +
|
| + SetInitialFocus();
|
| +}
|
| +
|
| +View* NativeWidgetWin::GetAccessibilityViewEventAt(int id) {
|
| + // Convert from MSAA child id.
|
| + id = -(id + 1);
|
| + DCHECK(id >= 0 && id < kMaxAccessibilityViewEvents);
|
| + return accessibility_view_events_[id];
|
| +}
|
| +
|
| +int NativeWidgetWin::AddAccessibilityViewEvent(View* view) {
|
| + accessibility_view_events_index_ =
|
| + (accessibility_view_events_index_ + 1) % kMaxAccessibilityViewEvents;
|
| + accessibility_view_events_[accessibility_view_events_index_] = view;
|
| +
|
| + // Convert to MSAA child id.
|
| + return -(accessibility_view_events_index_ + 1);
|
| +}
|
| +
|
| +void NativeWidgetWin::ClearAccessibilityViewEvent(View* view) {
|
| + for (std::vector<View*>::iterator it = accessibility_view_events_.begin();
|
| + it != accessibility_view_events_.end();
|
| + ++it) {
|
| + if (*it == view)
|
| + *it = NULL;
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::PushForceHidden() {
|
| + if (force_hidden_count_++ == 0)
|
| + Hide();
|
| +}
|
| +
|
| +void NativeWidgetWin::PopForceHidden() {
|
| + if (--force_hidden_count_ <= 0) {
|
| + force_hidden_count_ = 0;
|
| + ShowWindow(SW_SHOW);
|
| + }
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// NativeWidgetWin, CompositorDelegate implementation:
|
| +
|
| +void NativeWidgetWin::ScheduleDraw() {
|
| + RECT rect;
|
| + ::GetClientRect(GetNativeView(), &rect);
|
| + InvalidateRect(GetNativeView(), &rect, FALSE);
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// NativeWidgetWin, NativeWidget implementation:
|
| +
|
| +void NativeWidgetWin::InitNativeWidget(const Widget::InitParams& params) {
|
| + SetInitParams(params);
|
| +
|
| + GetMonitorAndRects(params.bounds.ToRECT(), &last_monitor_,
|
| + &last_monitor_rect_, &last_work_area_);
|
| +
|
| + // Create the window.
|
| + WindowImpl::Init(params.GetParent(), params.bounds);
|
| +}
|
| +
|
| +NonClientFrameView* NativeWidgetWin::CreateNonClientFrameView() {
|
| + return GetWidget()->ShouldUseNativeFrame() ?
|
| + new NativeFrameView(GetWidget()) : NULL;
|
| +}
|
| +
|
| +void NativeWidgetWin::UpdateFrameAfterFrameChange() {
|
| + // We've either gained or lost a custom window region, so reset it now.
|
| + ResetWindowRegion(true);
|
| +}
|
| +
|
| +bool NativeWidgetWin::ShouldUseNativeFrame() const {
|
| + return IsAeroGlassEnabled();
|
| +}
|
| +
|
| +void NativeWidgetWin::FrameTypeChanged() {
|
| + // Called when the frame type could possibly be changing (theme change or
|
| + // DWM composition change).
|
| + if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
|
| + // We need to toggle the rendering policy of the DWM/glass frame as we
|
| + // change from opaque to glass. "Non client rendering enabled" means that
|
| + // the DWM's glass non-client rendering is enabled, which is why
|
| + // DWMNCRP_ENABLED is used for the native frame case. _DISABLED means the
|
| + // DWM doesn't render glass, and so is used in the custom frame case.
|
| + DWMNCRENDERINGPOLICY policy = GetWidget()->ShouldUseNativeFrame() ?
|
| + DWMNCRP_ENABLED : DWMNCRP_DISABLED;
|
| + DwmSetWindowAttribute(GetNativeView(), DWMWA_NCRENDERING_POLICY,
|
| + &policy, sizeof(DWMNCRENDERINGPOLICY));
|
| + }
|
| +
|
| + // Send a frame change notification, since the non-client metrics have
|
| + // changed.
|
| + SendFrameChanged(GetNativeView());
|
| +
|
| + // Update the non-client view with the correct frame view for the active frame
|
| + // type.
|
| + GetWidget()->non_client_view()->UpdateFrame();
|
| +
|
| + // WM_DWMCOMPOSITIONCHANGED is only sent to top level windows, however we want
|
| + // to notify our children too, since we can have MDI child windows who need to
|
| + // update their appearance.
|
| + EnumChildWindows(GetNativeView(), &SendDwmCompositionChanged, NULL);
|
| +}
|
| +
|
| +Widget* NativeWidgetWin::GetWidget() {
|
| + return delegate_->AsWidget();
|
| +}
|
| +
|
| +const Widget* NativeWidgetWin::GetWidget() const {
|
| + return delegate_->AsWidget();
|
| +}
|
| +
|
| +gfx::NativeView NativeWidgetWin::GetNativeView() const {
|
| + return WindowImpl::hwnd();
|
| +}
|
| +
|
| +gfx::NativeWindow NativeWidgetWin::GetNativeWindow() const {
|
| + return WindowImpl::hwnd();
|
| +}
|
| +
|
| +Widget* NativeWidgetWin::GetTopLevelWidget() {
|
| + NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView());
|
| + return native_widget ? native_widget->GetWidget() : NULL;
|
| +}
|
| +
|
| +const ui::Compositor* NativeWidgetWin::GetCompositor() const {
|
| + return compositor_.get();
|
| +}
|
| +
|
| +ui::Compositor* NativeWidgetWin::GetCompositor() {
|
| + return compositor_.get();
|
| +}
|
| +
|
| +void NativeWidgetWin::CalculateOffsetToAncestorWithLayer(
|
| + gfx::Point* offset,
|
| + ui::Layer** layer_parent) {
|
| +}
|
| +
|
| +void NativeWidgetWin::ReorderLayers() {
|
| +}
|
| +
|
| +void NativeWidgetWin::ViewRemoved(View* view) {
|
| + if (drop_target_.get())
|
| + drop_target_->ResetTargetViewIfEquals(view);
|
| +
|
| + ClearAccessibilityViewEvent(view);
|
| +}
|
| +
|
| +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);
|
| +}
|
| +
|
| +TooltipManager* NativeWidgetWin::GetTooltipManager() const {
|
| + return tooltip_manager_.get();
|
| +}
|
| +
|
| +bool NativeWidgetWin::IsScreenReaderActive() const {
|
| + return screen_reader_active_;
|
| +}
|
| +
|
| +void NativeWidgetWin::SendNativeAccessibilityEvent(
|
| + View* view,
|
| + ui::AccessibilityTypes::Event event_type) {
|
| + // Now call the Windows-specific method to notify MSAA clients of this
|
| + // event. The widget gives us a temporary unique child ID to associate
|
| + // with this view so that clients can call get_accChild in
|
| + // NativeViewAccessibilityWin to retrieve the IAccessible associated
|
| + // with this view.
|
| + int child_id = AddAccessibilityViewEvent(view);
|
| + ::NotifyWinEvent(NativeViewAccessibilityWin::MSAAEvent(event_type),
|
| + GetNativeView(), OBJID_CLIENT, child_id);
|
| +}
|
| +
|
| +void NativeWidgetWin::SetMouseCapture() {
|
| + DCHECK(!HasMouseCapture());
|
| + SetCapture(hwnd());
|
| +}
|
| +
|
| +void NativeWidgetWin::ReleaseMouseCapture() {
|
| + ReleaseCapture();
|
| +}
|
| +
|
| +bool NativeWidgetWin::HasMouseCapture() const {
|
| + return GetCapture() == hwnd();
|
| +}
|
| +
|
| +InputMethod* NativeWidgetWin::CreateInputMethod() {
|
| + if (views::Widget::IsPureViews()) {
|
| + InputMethod* input_method = new InputMethodWin(this);
|
| + input_method->Init(GetWidget());
|
| + return input_method;
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +void NativeWidgetWin::CenterWindow(const gfx::Size& size) {
|
| + HWND parent = GetParent();
|
| + if (!IsWindow())
|
| + parent = ::GetWindow(GetNativeView(), GW_OWNER);
|
| + ui::CenterAndSizeWindow(parent, GetNativeView(), size, false);
|
| +}
|
| +
|
| +void NativeWidgetWin::GetWindowPlacement(
|
| + gfx::Rect* bounds,
|
| + ui::WindowShowState* show_state) const {
|
| + WINDOWPLACEMENT wp;
|
| + wp.length = sizeof(wp);
|
| + const bool succeeded = !!::GetWindowPlacement(GetNativeView(), &wp);
|
| + DCHECK(succeeded);
|
| +
|
| + if (bounds != NULL) {
|
| + MONITORINFO mi;
|
| + mi.cbSize = sizeof(mi);
|
| + const bool succeeded = !!GetMonitorInfo(
|
| + MonitorFromWindow(GetNativeView(), MONITOR_DEFAULTTONEAREST), &mi);
|
| + DCHECK(succeeded);
|
| + *bounds = gfx::Rect(wp.rcNormalPosition);
|
| + // Convert normal position from workarea coordinates to screen coordinates.
|
| + bounds->Offset(mi.rcWork.left - mi.rcMonitor.left,
|
| + mi.rcWork.top - mi.rcMonitor.top);
|
| + }
|
| +
|
| + if (show_state != NULL) {
|
| + if (wp.showCmd == SW_SHOWMAXIMIZED)
|
| + *show_state = ui::SHOW_STATE_MAXIMIZED;
|
| + else if (wp.showCmd == SW_SHOWMINIMIZED)
|
| + *show_state = ui::SHOW_STATE_MINIMIZED;
|
| + else
|
| + *show_state = ui::SHOW_STATE_NORMAL;
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::SetWindowTitle(const string16& title) {
|
| + SetWindowText(GetNativeView(), title.c_str());
|
| + SetAccessibleName(title);
|
| +}
|
| +
|
| +void NativeWidgetWin::SetWindowIcons(const SkBitmap& window_icon,
|
| + const SkBitmap& app_icon) {
|
| + if (!window_icon.isNull()) {
|
| + HICON windows_icon = IconUtil::CreateHICONFromSkBitmap(window_icon);
|
| + // We need to make sure to destroy the previous icon, otherwise we'll leak
|
| + // these GDI objects until we crash!
|
| + HICON old_icon = reinterpret_cast<HICON>(
|
| + SendMessage(GetNativeView(), WM_SETICON, ICON_SMALL,
|
| + reinterpret_cast<LPARAM>(windows_icon)));
|
| + if (old_icon)
|
| + DestroyIcon(old_icon);
|
| + }
|
| + if (!app_icon.isNull()) {
|
| + HICON windows_icon = IconUtil::CreateHICONFromSkBitmap(app_icon);
|
| + HICON old_icon = reinterpret_cast<HICON>(
|
| + SendMessage(GetNativeView(), WM_SETICON, ICON_BIG,
|
| + reinterpret_cast<LPARAM>(windows_icon)));
|
| + if (old_icon)
|
| + DestroyIcon(old_icon);
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::SetAccessibleName(const string16& name) {
|
| + base::win::ScopedComPtr<IAccPropServices> pAccPropServices;
|
| + HRESULT hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER,
|
| + IID_IAccPropServices, reinterpret_cast<void**>(&pAccPropServices));
|
| + if (SUCCEEDED(hr))
|
| + hr = pAccPropServices->SetHwndPropStr(GetNativeView(), OBJID_CLIENT,
|
| + CHILDID_SELF, PROPID_ACC_NAME,
|
| + name.c_str());
|
| +}
|
| +
|
| +void NativeWidgetWin::SetAccessibleRole(ui::AccessibilityTypes::Role role) {
|
| + base::win::ScopedComPtr<IAccPropServices> pAccPropServices;
|
| + HRESULT hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER,
|
| + IID_IAccPropServices, reinterpret_cast<void**>(&pAccPropServices));
|
| + if (SUCCEEDED(hr)) {
|
| + VARIANT var;
|
| + if (role) {
|
| + var.vt = VT_I4;
|
| + var.lVal = NativeViewAccessibilityWin::MSAARole(role);
|
| + hr = pAccPropServices->SetHwndProp(GetNativeView(), OBJID_CLIENT,
|
| + CHILDID_SELF, PROPID_ACC_ROLE, var);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::SetAccessibleState(ui::AccessibilityTypes::State state) {
|
| + base::win::ScopedComPtr<IAccPropServices> pAccPropServices;
|
| + HRESULT hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER,
|
| + IID_IAccPropServices, reinterpret_cast<void**>(&pAccPropServices));
|
| + if (SUCCEEDED(hr)) {
|
| + VARIANT var;
|
| + if (state) {
|
| + var.vt = VT_I4;
|
| + var.lVal = NativeViewAccessibilityWin::MSAAState(state);
|
| + hr = pAccPropServices->SetHwndProp(GetNativeView(), OBJID_CLIENT,
|
| + CHILDID_SELF, PROPID_ACC_STATE, var);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::BecomeModal() {
|
| + // We implement modality by crawling up the hierarchy of windows starting
|
| + // at the owner, disabling all of them so that they don't receive input
|
| + // messages.
|
| + HWND start = ::GetWindow(GetNativeView(), GW_OWNER);
|
| + while (start) {
|
| + ::EnableWindow(start, FALSE);
|
| + start = ::GetParent(start);
|
| + }
|
| +}
|
| +
|
| +gfx::Rect NativeWidgetWin::GetWindowScreenBounds() const {
|
| + RECT r;
|
| + GetWindowRect(&r);
|
| + return gfx::Rect(r);
|
| +}
|
| +
|
| +gfx::Rect NativeWidgetWin::GetClientAreaScreenBounds() const {
|
| + RECT r;
|
| + GetClientRect(&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);
|
| +}
|
| +
|
| +gfx::Rect NativeWidgetWin::GetRestoredBounds() const {
|
| + // If we're in fullscreen mode, we've changed the normal bounds to the monitor
|
| + // rect, so return the saved bounds instead.
|
| + if (IsFullscreen())
|
| + return gfx::Rect(saved_window_info_.window_rect);
|
| +
|
| + gfx::Rect bounds;
|
| + GetWindowPlacement(&bounds, NULL);
|
| + return bounds;
|
| +}
|
| +
|
| +void NativeWidgetWin::SetBounds(const gfx::Rect& bounds) {
|
| + LONG style = GetWindowLong(GWL_STYLE);
|
| + if (style & WS_MAXIMIZE)
|
| + SetWindowLong(GWL_STYLE, style & ~WS_MAXIMIZE);
|
| + SetWindowPos(NULL, bounds.x(), bounds.y(), bounds.width(), bounds.height(),
|
| + SWP_NOACTIVATE | SWP_NOZORDER);
|
| +}
|
| +
|
| +void NativeWidgetWin::SetSize(const gfx::Size& size) {
|
| + SetWindowPos(NULL, 0, 0, size.width(), size.height(),
|
| + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
|
| +}
|
| +
|
| +void NativeWidgetWin::MoveAbove(gfx::NativeView native_view) {
|
| + SetWindowPos(native_view, 0, 0, 0, 0,
|
| + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
|
| +}
|
| +
|
| +void NativeWidgetWin::MoveToTop() {
|
| + NOTIMPLEMENTED();
|
| +}
|
| +
|
| +void NativeWidgetWin::SetShape(gfx::NativeRegion region) {
|
| + SetWindowRgn(region, TRUE);
|
| +}
|
| +
|
| +void NativeWidgetWin::Close() {
|
| + if (!IsWindow())
|
| + return; // No need to do anything.
|
| +
|
| + // Let's hide ourselves right away.
|
| + Hide();
|
| +
|
| + // Modal dialog windows disable their owner windows; re-enable them now so
|
| + // they can activate as foreground windows upon this window's destruction.
|
| + RestoreEnabledIfNecessary();
|
| +
|
| + if (!close_widget_factory_.HasWeakPtrs()) {
|
| + // And we delay the close so that if we are called from an ATL callback,
|
| + // we don't destroy the window before the callback returned (as the caller
|
| + // may delete ourselves on destroy and the ATL callback would still
|
| + // dereference us when the callback returns).
|
| + MessageLoop::current()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&NativeWidgetWin::CloseNow,
|
| + close_widget_factory_.GetWeakPtr()));
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::CloseNow() {
|
| + // We may already have been destroyed if the selection resulted in a tab
|
| + // switch which will have reactivated the browser window and closed us, so
|
| + // we need to check to see if we're still a window before trying to destroy
|
| + // ourself.
|
| + if (IsWindow())
|
| + DestroyWindow(hwnd());
|
| +}
|
| +
|
| +void NativeWidgetWin::EnableClose(bool enable) {
|
| + // Disable the native frame's close button regardless of whether or not the
|
| + // native frame is in use, since this also affects the system menu.
|
| + EnableMenuItem(GetSystemMenu(GetNativeView(), false), SC_CLOSE, enable);
|
| + SendFrameChanged(GetNativeView());
|
| +}
|
| +
|
| +void NativeWidgetWin::Show() {
|
| + if (!IsWindow())
|
| + return;
|
| +
|
| + ShowWindow(SW_SHOWNOACTIVATE);
|
| + SetInitialFocus();
|
| +}
|
| +
|
| +void NativeWidgetWin::Hide() {
|
| + if (IsWindow()) {
|
| + // 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(NULL, 0, 0, 0, 0,
|
| + SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE |
|
| + SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER);
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::ShowMaximizedWithBounds(
|
| + const gfx::Rect& restored_bounds) {
|
| + WINDOWPLACEMENT placement = { 0 };
|
| + placement.length = sizeof(WINDOWPLACEMENT);
|
| + placement.showCmd = SW_SHOWMAXIMIZED;
|
| + placement.rcNormalPosition = restored_bounds.ToRECT();
|
| + SetWindowPlacement(hwnd(), &placement);
|
| +}
|
| +
|
| +void NativeWidgetWin::ShowWithWindowState(ui::WindowShowState show_state) {
|
| + DWORD native_show_state;
|
| + switch (show_state) {
|
| + case ui::SHOW_STATE_INACTIVE:
|
| + native_show_state = SW_SHOWNOACTIVATE;
|
| + break;
|
| + case ui::SHOW_STATE_MAXIMIZED:
|
| + native_show_state = SW_SHOWMAXIMIZED;
|
| + break;
|
| + case ui::SHOW_STATE_MINIMIZED:
|
| + native_show_state = SW_SHOWMINIMIZED;
|
| + break;
|
| + default:
|
| + native_show_state = GetShowState();
|
| + break;
|
| + }
|
| + Show(native_show_state);
|
| +}
|
| +
|
| +bool NativeWidgetWin::IsVisible() const {
|
| + return !!::IsWindowVisible(hwnd());
|
| +}
|
| +
|
| +void NativeWidgetWin::Activate() {
|
| + if (IsMinimized())
|
| + ::ShowWindow(GetNativeView(), SW_RESTORE);
|
| + ::SetWindowPos(GetNativeView(), HWND_TOP, 0, 0, 0, 0,
|
| + SWP_NOSIZE | SWP_NOMOVE);
|
| + SetForegroundWindow(GetNativeView());
|
| +}
|
| +
|
| +void NativeWidgetWin::Deactivate() {
|
| + HWND hwnd = ::GetNextWindow(GetNativeView(), GW_HWNDNEXT);
|
| + if (hwnd)
|
| + ::SetForegroundWindow(hwnd);
|
| +}
|
| +
|
| +bool NativeWidgetWin::IsActive() const {
|
| + return GetActiveWindow() == hwnd();
|
| +}
|
| +
|
| +void NativeWidgetWin::SetAlwaysOnTop(bool on_top) {
|
| + ::SetWindowPos(GetNativeView(), on_top ? HWND_TOPMOST : HWND_NOTOPMOST,
|
| + 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
| +}
|
| +
|
| +void NativeWidgetWin::Maximize() {
|
| + ExecuteSystemMenuCommand(SC_MAXIMIZE);
|
| +}
|
| +
|
| +void NativeWidgetWin::Minimize() {
|
| + ExecuteSystemMenuCommand(SC_MINIMIZE);
|
| +
|
| + delegate_->OnNativeBlur(NULL);
|
| +}
|
| +
|
| +bool NativeWidgetWin::IsMaximized() const {
|
| + return !!::IsZoomed(GetNativeView());
|
| +}
|
| +
|
| +bool NativeWidgetWin::IsMinimized() const {
|
| + return !!::IsIconic(GetNativeView());
|
| +}
|
| +
|
| +void NativeWidgetWin::Restore() {
|
| + ExecuteSystemMenuCommand(SC_RESTORE);
|
| +}
|
| +
|
| +void NativeWidgetWin::SetFullscreen(bool fullscreen) {
|
| + if (fullscreen_ == fullscreen)
|
| + return; // Nothing to do.
|
| +
|
| + // Reduce jankiness during the following position changes by hiding the window
|
| + // until it's in the final position.
|
| + PushForceHidden();
|
| +
|
| + // Size/position/style window appropriately.
|
| + if (!fullscreen_) {
|
| + // Save current window information. We force the window into restored mode
|
| + // before going fullscreen because Windows doesn't seem to hide the
|
| + // taskbar if the window is in the maximized state.
|
| + saved_window_info_.maximized = IsMaximized();
|
| + if (saved_window_info_.maximized)
|
| + Restore();
|
| + saved_window_info_.style = GetWindowLong(GWL_STYLE);
|
| + saved_window_info_.ex_style = GetWindowLong(GWL_EXSTYLE);
|
| + GetWindowRect(&saved_window_info_.window_rect);
|
| + }
|
| +
|
| + fullscreen_ = fullscreen;
|
| +
|
| + if (fullscreen_) {
|
| + // Set new window style and size.
|
| + MONITORINFO monitor_info;
|
| + monitor_info.cbSize = sizeof(monitor_info);
|
| + GetMonitorInfo(MonitorFromWindow(GetNativeView(), MONITOR_DEFAULTTONEAREST),
|
| + &monitor_info);
|
| + gfx::Rect monitor_rect(monitor_info.rcMonitor);
|
| + SetWindowLong(GWL_STYLE,
|
| + saved_window_info_.style & ~(WS_CAPTION | WS_THICKFRAME));
|
| + SetWindowLong(GWL_EXSTYLE,
|
| + saved_window_info_.ex_style & ~(WS_EX_DLGMODALFRAME |
|
| + WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
|
| + SetWindowPos(NULL, monitor_rect.x(), monitor_rect.y(),
|
| + monitor_rect.width(), monitor_rect.height(),
|
| + SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
|
| + } else {
|
| + // Reset original window style and size. The multiple window size/moves
|
| + // here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be
|
| + // repainted. Better-looking methods welcome.
|
| + gfx::Rect new_rect(saved_window_info_.window_rect);
|
| + SetWindowLong(GWL_STYLE, saved_window_info_.style);
|
| + SetWindowLong(GWL_EXSTYLE, saved_window_info_.ex_style);
|
| + SetWindowPos(NULL, new_rect.x(), new_rect.y(), new_rect.width(),
|
| + new_rect.height(),
|
| + SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
|
| + if (saved_window_info_.maximized)
|
| + Maximize();
|
| + }
|
| +
|
| + // Undo our anti-jankiness hacks.
|
| + PopForceHidden();
|
| +}
|
| +
|
| +bool NativeWidgetWin::IsFullscreen() const {
|
| + return fullscreen_;
|
| +}
|
| +
|
| +void NativeWidgetWin::SetOpacity(unsigned char opacity) {
|
| + layered_alpha_ = static_cast<BYTE>(opacity);
|
| +}
|
| +
|
| +void NativeWidgetWin::SetUseDragFrame(bool use_drag_frame) {
|
| + if (use_drag_frame) {
|
| + // Make the frame slightly transparent during the drag operation.
|
| + drag_frame_saved_window_style_ = GetWindowLong(GWL_STYLE);
|
| + drag_frame_saved_window_ex_style_ = GetWindowLong(GWL_EXSTYLE);
|
| + SetWindowLong(GWL_EXSTYLE,
|
| + drag_frame_saved_window_ex_style_ | WS_EX_LAYERED);
|
| + // Remove the captions tyle so the window doesn't have window controls for a
|
| + // more "transparent" look.
|
| + SetWindowLong(GWL_STYLE, drag_frame_saved_window_style_ & ~WS_CAPTION);
|
| + SetLayeredWindowAttributes(GetNativeWindow(), RGB(0xFF, 0xFF, 0xFF),
|
| + kDragFrameWindowAlpha, LWA_ALPHA);
|
| + } else {
|
| + SetWindowLong(GWL_STYLE, drag_frame_saved_window_style_);
|
| + SetWindowLong(GWL_EXSTYLE, drag_frame_saved_window_ex_style_);
|
| + }
|
| +}
|
| +
|
| +bool NativeWidgetWin::IsAccessibleWidget() const {
|
| + return screen_reader_active_;
|
| +}
|
| +
|
| +void NativeWidgetWin::RunShellDrag(View* view,
|
| + const ui::OSExchangeData& data,
|
| + int operation) {
|
| + scoped_refptr<ui::DragSource> drag_source(new ui::DragSource);
|
| + DWORD effects;
|
| + DoDragDrop(ui::OSExchangeDataProviderWin::GetIDataObject(data), drag_source,
|
| + ui::DragDropTypes::DragOperationToDropEffect(operation), &effects);
|
| +}
|
| +
|
| +void NativeWidgetWin::SchedulePaintInRect(const gfx::Rect& rect) {
|
| + if (use_layered_buffer_) {
|
| + // We must update the back-buffer immediately, since Windows' handling of
|
| + // invalid rects is somewhat mysterious.
|
| + invalid_rect_ = invalid_rect_.Union(rect);
|
| +
|
| + // In some situations, such as drag and drop, when Windows itself runs a
|
| + // nested message loop our message loop appears to be starved and we don't
|
| + // receive calls to DidProcessMessage(). This only seems to affect layered
|
| + // windows, so we schedule a redraw manually using a task, since those never
|
| + // seem to be starved. Also, wtf.
|
| + if (!paint_layered_window_factory_.HasWeakPtrs()) {
|
| + MessageLoop::current()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&NativeWidgetWin::RedrawLayeredWindowContents,
|
| + paint_layered_window_factory_.GetWeakPtr()));
|
| + }
|
| + } else {
|
| + // InvalidateRect() expects client coordinates.
|
| + RECT r = rect.ToRECT();
|
| + InvalidateRect(hwnd(), &r, FALSE);
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::SetCursor(gfx::NativeCursor cursor) {
|
| + if (cursor) {
|
| + previous_cursor_ = ::SetCursor(cursor);
|
| + } else if (previous_cursor_) {
|
| + ::SetCursor(previous_cursor_);
|
| + previous_cursor_ = NULL;
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::ClearNativeFocus() {
|
| + ::SetFocus(GetNativeView());
|
| +}
|
| +
|
| +void NativeWidgetWin::FocusNativeView(gfx::NativeView native_view) {
|
| + // Only reset focus if hwnd is not already focused.
|
| + if (native_view && ::GetFocus() != native_view)
|
| + ::SetFocus(native_view);
|
| +}
|
| +
|
| +bool NativeWidgetWin::ConvertPointFromAncestor(
|
| + const Widget* ancestor, gfx::Point* point) const {
|
| + NOTREACHED();
|
| + return false;
|
| +}
|
| +
|
| +gfx::Rect NativeWidgetWin::GetWorkAreaBoundsInScreen() const {
|
| + return gfx::Screen::GetMonitorWorkAreaNearestWindow(GetNativeView());
|
| +}
|
| +
|
| +void NativeWidgetWin::SetInactiveRenderingDisabled(bool value) {
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// NativeWidgetWin, MessageLoop::Observer implementation:
|
| +
|
| +base::EventStatus NativeWidgetWin::WillProcessEvent(
|
| + const base::NativeEvent& event) {
|
| + return base::EVENT_CONTINUE;
|
| +}
|
| +
|
| +void NativeWidgetWin::DidProcessEvent(const base::NativeEvent& event) {
|
| + RedrawInvalidRect();
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// NativeWidgetWin, WindowImpl overrides:
|
| +
|
| +HICON NativeWidgetWin::GetDefaultWindowIcon() const {
|
| + if (ViewsDelegate::views_delegate)
|
| + return ViewsDelegate::views_delegate->GetDefaultWindowIcon();
|
| + return NULL;
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnWndProc(UINT message, WPARAM w_param,
|
| + LPARAM l_param) {
|
| + HWND window = hwnd();
|
| + LRESULT result = 0;
|
| +
|
| + // First allow messages sent by child controls to be processed directly by
|
| + // their associated views. If such a view is present, it will handle the
|
| + // message *instead of* this NativeWidgetWin.
|
| + if (ProcessChildWindowMessage(message, w_param, l_param, &result))
|
| + return result;
|
| +
|
| + // Otherwise we handle everything else.
|
| + if (!ProcessWindowMessage(window, message, w_param, l_param, result))
|
| + result = DefWindowProc(window, message, w_param, l_param);
|
| + if (message == WM_NCDESTROY) {
|
| + MessageLoopForUI::current()->RemoveObserver(this);
|
| + OnFinalMessage(window);
|
| + }
|
| +
|
| + // Only top level widget should store/restore focus.
|
| + if (message == WM_ACTIVATE && GetWidget()->is_top_level())
|
| + PostProcessActivateMessage(this, LOWORD(w_param));
|
| + if (message == WM_ENABLE && restore_focus_when_enabled_) {
|
| + // This path should be executed only for top level as
|
| + // restore_focus_when_enabled_ is set in PostProcessActivateMessage.
|
| + DCHECK(GetWidget()->is_top_level());
|
| + restore_focus_when_enabled_ = false;
|
| + GetWidget()->GetFocusManager()->RestoreFocusedView();
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// NativeWidgetWin, protected:
|
| +
|
| +// Message handlers ------------------------------------------------------------
|
| +
|
| +void NativeWidgetWin::OnActivate(UINT action, BOOL minimized, HWND window) {
|
| + SetMsgHandled(FALSE);
|
| +}
|
| +
|
| +void NativeWidgetWin::OnActivateApp(BOOL active, DWORD thread_id) {
|
| + if (GetWidget()->non_client_view() && !active &&
|
| + thread_id != GetCurrentThreadId()) {
|
| + // Another application was activated, we should reset any state that
|
| + // disables inactive rendering now.
|
| + delegate_->EnableInactiveRendering();
|
| + // Also update the native frame if it is rendering the non-client area.
|
| + if (GetWidget()->ShouldUseNativeFrame())
|
| + DefWindowProcWithRedrawLock(WM_NCACTIVATE, FALSE, 0);
|
| + }
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnAppCommand(HWND window,
|
| + short app_command,
|
| + WORD device,
|
| + int keystate) {
|
| + // We treat APPCOMMAND ids as an extension of our command namespace, and just
|
| + // let the delegate figure out what to do...
|
| + BOOL is_handled = (GetWidget()->widget_delegate() &&
|
| + GetWidget()->widget_delegate()->ExecuteWindowsCommand(app_command)) ?
|
| + TRUE : FALSE;
|
| + SetMsgHandled(is_handled);
|
| + // Make sure to return TRUE if the event was handled or in some cases the
|
| + // system will execute the default handler which can cause bugs like going
|
| + // forward or back two pages instead of one.
|
| + return is_handled;
|
| +}
|
| +
|
| +void NativeWidgetWin::OnCancelMode() {
|
| + SetMsgHandled(FALSE);
|
| +}
|
| +
|
| +void NativeWidgetWin::OnCaptureChanged(HWND hwnd) {
|
| + delegate_->OnMouseCaptureLost();
|
| +}
|
| +
|
| +void NativeWidgetWin::OnClose() {
|
| + GetWidget()->Close();
|
| +}
|
| +
|
| +void NativeWidgetWin::OnCommand(UINT notification_code,
|
| + int command_id,
|
| + HWND window) {
|
| + // If the notification code is > 1 it means it is control specific and we
|
| + // should ignore it.
|
| + if (notification_code > 1 ||
|
| + GetWidget()->widget_delegate()->ExecuteWindowsCommand(command_id)) {
|
| + SetMsgHandled(FALSE);
|
| + }
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnCreate(CREATESTRUCT* create_struct) {
|
| + SetNativeWindowProperty(kNativeWidgetKey, this);
|
| + CHECK_EQ(this, GetNativeWidgetForNativeView(hwnd()));
|
| +
|
| + use_layered_buffer_ = !!(window_ex_style() & WS_EX_LAYERED);
|
| +
|
| + // Attempt to detect screen readers by sending an event with our custom id.
|
| + if (!IsAccessibleWidget())
|
| + NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd(), kCustomObjectID, CHILDID_SELF);
|
| +
|
| + props_.push_back(ui::SetWindowSupportsRerouteMouseWheel(hwnd()));
|
| +
|
| + drop_target_ = new DropTargetWin(
|
| + static_cast<internal::RootView*>(GetWidget()->GetRootView()));
|
| +
|
| + // 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.
|
| + MessageLoopForUI::current()->AddObserver(this);
|
| +
|
| + // Windows special DWM window frame requires a special tooltip manager so
|
| + // that window controls in Chrome windows don't flicker when you move your
|
| + // mouse over them. See comment in aero_tooltip_manager.h.
|
| + Widget* widget = GetWidget()->GetTopLevelWidget();
|
| + if (widget && widget->ShouldUseNativeFrame()) {
|
| + tooltip_manager_.reset(new AeroTooltipManager(GetWidget()));
|
| + } else {
|
| + tooltip_manager_.reset(new TooltipManagerWin(GetWidget()));
|
| + }
|
| + if (!tooltip_manager_->Init()) {
|
| + // There was a problem creating the TooltipManager. Common error is 127.
|
| + // See 82193 for details.
|
| + LOG_GETLASTERROR(WARNING) << "tooltip creation failed, disabling tooltips";
|
| + tooltip_manager_.reset();
|
| + }
|
| +
|
| + // This message initializes the window so that focus border are shown for
|
| + // windows.
|
| + SendMessage(
|
| + hwnd(), WM_CHANGEUISTATE, MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);
|
| +
|
| + // Bug 964884: detach the IME attached to this window.
|
| + // We should attach IMEs only when we need to input CJK strings.
|
| + ImmAssociateContextEx(hwnd(), NULL, 0);
|
| +
|
| + // We need to allow the delegate to size its contents since the window may not
|
| + // receive a size notification when its initial bounds are specified at window
|
| + // creation time.
|
| + ClientAreaSizeChanged();
|
| +
|
| +#if defined(VIEWS_COMPOSITOR)
|
| + if (View::get_use_acceleration_when_possible()) {
|
| + if (ui::Compositor::compositor_factory()) {
|
| + compositor_ = (*Widget::compositor_factory())(this);
|
| + } else {
|
| + CRect window_rect;
|
| + GetClientRect(&window_rect);
|
| + compositor_ = ui::Compositor::Create(this,
|
| + hwnd(),
|
| + gfx::Size(window_rect.Width(), window_rect.Height()));
|
| + }
|
| + if (compositor_.get()) {
|
| + delegate_->AsWidget()->GetRootView()->SetPaintToLayer(true);
|
| + compositor_->SetRootLayer(delegate_->AsWidget()->GetRootView()->layer());
|
| + }
|
| + }
|
| +#endif
|
| +
|
| + delegate_->OnNativeWidgetCreated();
|
| +
|
| + // Get access to a modifiable copy of the system menu.
|
| + GetSystemMenu(hwnd(), false);
|
| + return 0;
|
| +}
|
| +
|
| +void NativeWidgetWin::OnDestroy() {
|
| + delegate_->OnNativeWidgetDestroying();
|
| + if (drop_target_.get()) {
|
| + RevokeDragDrop(hwnd());
|
| + drop_target_ = NULL;
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::OnDisplayChange(UINT bits_per_pixel, CSize screen_size) {
|
| + GetWidget()->widget_delegate()->OnDisplayChanged();
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnDwmCompositionChanged(UINT msg,
|
| + WPARAM w_param,
|
| + LPARAM l_param) {
|
| + if (!GetWidget()->non_client_view()) {
|
| + SetMsgHandled(FALSE);
|
| + return 0;
|
| + }
|
| +
|
| + // For some reason, we need to hide the window while we're changing the frame
|
| + // type only when we're changing it in response to WM_DWMCOMPOSITIONCHANGED.
|
| + // If we don't, the client area will be filled with black. I'm suspecting
|
| + // something skia-ey.
|
| + // Frame type toggling caused by the user (e.g. switching theme) doesn't seem
|
| + // to have this requirement.
|
| + FrameTypeChanged();
|
| + return 0;
|
| +}
|
| +
|
| +void NativeWidgetWin::OnEndSession(BOOL ending, UINT logoff) {
|
| + SetMsgHandled(FALSE);
|
| +}
|
| +
|
| +void NativeWidgetWin::OnEnterSizeMove() {
|
| + delegate_->OnNativeWidgetBeginUserBoundsChange();
|
| + 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() {
|
| + delegate_->OnNativeWidgetEndUserBoundsChange();
|
| + SetMsgHandled(FALSE);
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnGetObject(UINT uMsg,
|
| + WPARAM w_param,
|
| + LPARAM l_param) {
|
| + LRESULT reference_result = static_cast<LRESULT>(0L);
|
| +
|
| + // Accessibility readers will send an OBJID_CLIENT message
|
| + if (OBJID_CLIENT == l_param) {
|
| + // Retrieve MSAA dispatch object for the root view.
|
| + base::win::ScopedComPtr<IAccessible> root(
|
| + GetWidget()->GetRootView()->GetNativeViewAccessible());
|
| +
|
| + // Create a reference that MSAA will marshall to the client.
|
| + reference_result = LresultFromObject(IID_IAccessible, w_param,
|
| + static_cast<IAccessible*>(root.Detach()));
|
| + }
|
| +
|
| + if (kCustomObjectID == l_param) {
|
| + // An MSAA client requestes our custom id. Assume that we have detected an
|
| + // active windows screen reader.
|
| + OnScreenReaderDetected();
|
| +
|
| + // Return with failure.
|
| + return static_cast<LRESULT>(0L);
|
| + }
|
| +
|
| + return reference_result;
|
| +}
|
| +
|
| +void NativeWidgetWin::OnGetMinMaxInfo(MINMAXINFO* minmax_info) {
|
| + gfx::Size min_window_size(delegate_->GetMinimumSize());
|
| + // Add the native frame border size to the minimum size if the view reports
|
| + // its size as the client size.
|
| + if (WidgetSizeIsClientSize()) {
|
| + CRect client_rect, window_rect;
|
| + GetClientRect(&client_rect);
|
| + GetWindowRect(&window_rect);
|
| + window_rect -= client_rect;
|
| + min_window_size.Enlarge(window_rect.Width(), window_rect.Height());
|
| + }
|
| + minmax_info->ptMinTrackSize.x = min_window_size.width();
|
| + minmax_info->ptMinTrackSize.y = min_window_size.height();
|
| + SetMsgHandled(FALSE);
|
| +}
|
| +
|
| +void NativeWidgetWin::OnHScroll(int scroll_type,
|
| + short position,
|
| + HWND scrollbar) {
|
| + SetMsgHandled(FALSE);
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnImeMessages(UINT message,
|
| + WPARAM w_param,
|
| + LPARAM l_param) {
|
| + InputMethod* input_method = GetWidget()->GetInputMethodDirect();
|
| + if (!input_method || input_method->IsMock()) {
|
| + SetMsgHandled(FALSE);
|
| + return 0;
|
| + }
|
| +
|
| + InputMethodWin* ime_win = static_cast<InputMethodWin*>(input_method);
|
| + BOOL handled = FALSE;
|
| + LRESULT result = ime_win->OnImeMessages(message, w_param, l_param, &handled);
|
| +
|
| + SetMsgHandled(handled);
|
| + return result;
|
| +}
|
| +
|
| +void NativeWidgetWin::OnInitMenu(HMENU menu) {
|
| + bool is_fullscreen = IsFullscreen();
|
| + bool is_minimized = IsMinimized();
|
| + bool is_maximized = IsMaximized();
|
| + bool is_restored = !is_fullscreen && !is_minimized && !is_maximized;
|
| +
|
| + ScopedRedrawLock lock(this);
|
| + EnableMenuItem(menu, SC_RESTORE, is_minimized || is_maximized);
|
| + EnableMenuItem(menu, SC_MOVE, is_restored);
|
| + EnableMenuItem(menu, SC_SIZE,
|
| + GetWidget()->widget_delegate()->CanResize() && is_restored);
|
| + EnableMenuItem(menu, SC_MAXIMIZE,
|
| + GetWidget()->widget_delegate()->CanMaximize() &&
|
| + !is_fullscreen && !is_maximized);
|
| + EnableMenuItem(menu, SC_MINIMIZE,
|
| + GetWidget()->widget_delegate()->CanMaximize() &&
|
| + !is_minimized);
|
| +}
|
| +
|
| +void NativeWidgetWin::OnInitMenuPopup(HMENU menu,
|
| + UINT position,
|
| + BOOL is_system_menu) {
|
| + SetMsgHandled(FALSE);
|
| +}
|
| +
|
| +void NativeWidgetWin::OnInputLangChange(DWORD character_set,
|
| + HKL input_language_id) {
|
| + InputMethod* input_method = GetWidget()->GetInputMethodDirect();
|
| +
|
| + if (input_method && !input_method->IsMock()) {
|
| + static_cast<InputMethodWin*>(input_method)->OnInputLangChange(
|
| + character_set, input_language_id);
|
| + }
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnKeyEvent(UINT message,
|
| + WPARAM w_param,
|
| + LPARAM l_param) {
|
| + MSG msg = { hwnd(), message, w_param, l_param };
|
| + KeyEvent key(msg);
|
| + InputMethod* input_method = GetWidget()->GetInputMethodDirect();
|
| + if (input_method)
|
| + input_method->DispatchKeyEvent(key);
|
| + else
|
| + DispatchKeyEventPostIME(key);
|
| + return 0;
|
| +}
|
| +
|
| +void NativeWidgetWin::OnKillFocus(HWND focused_window) {
|
| + delegate_->OnNativeBlur(focused_window);
|
| + InputMethod* input_method = GetWidget()->GetInputMethodDirect();
|
| + if (input_method)
|
| + input_method->OnBlur();
|
| + SetMsgHandled(FALSE);
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnMouseActivate(UINT message,
|
| + WPARAM w_param,
|
| + LPARAM l_param) {
|
| + // TODO(beng): resolve this with the GetWindowLong() check on the subsequent
|
| + // line.
|
| + if (GetWidget()->non_client_view())
|
| + return delegate_->CanActivate() ? MA_ACTIVATE : MA_NOACTIVATEANDEAT;
|
| + if (GetWindowLong(GWL_EXSTYLE) & WS_EX_NOACTIVATE)
|
| + return MA_NOACTIVATE;
|
| + SetMsgHandled(FALSE);
|
| + return MA_ACTIVATE;
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnMouseRange(UINT message,
|
| + WPARAM w_param,
|
| + LPARAM l_param) {
|
| + if (message == WM_RBUTTONUP && is_right_mouse_pressed_on_caption_) {
|
| + is_right_mouse_pressed_on_caption_ = false;
|
| + ReleaseCapture();
|
| + // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu()
|
| + // expect screen coordinates.
|
| + CPoint screen_point(l_param);
|
| + MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_point, 1);
|
| + w_param = SendMessage(GetNativeView(), WM_NCHITTEST, 0,
|
| + MAKELPARAM(screen_point.x, screen_point.y));
|
| + if (w_param == HTCAPTION || w_param == HTSYSMENU) {
|
| + ui::ShowSystemMenu(GetNativeView(), screen_point.x, screen_point.y);
|
| + return 0;
|
| + }
|
| + } else if (message == WM_NCLBUTTONDOWN &&
|
| + !GetWidget()->ShouldUseNativeFrame()) {
|
| + switch (w_param) {
|
| + case HTCLOSE:
|
| + case HTMINBUTTON:
|
| + case HTMAXBUTTON: {
|
| + // When the mouse is pressed down in these specific non-client areas,
|
| + // we need to tell the RootView to send the mouse pressed event (which
|
| + // sets capture, allowing subsequent WM_LBUTTONUP (note, _not_
|
| + // WM_NCLBUTTONUP) to fire so that the appropriate WM_SYSCOMMAND can be
|
| + // sent by the applicable button's ButtonListener. We _have_ to do this
|
| + // way rather than letting Windows just send the syscommand itself (as
|
| + // would happen if we never did this dance) because for some insane
|
| + // reason DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed
|
| + // window control button appearance, in the Windows classic style, over
|
| + // our view! Ick! By handling this message we prevent Windows from
|
| + // doing this undesirable thing, but that means we need to roll the
|
| + // sys-command handling ourselves.
|
| + // Combine |w_param| with common key state message flags.
|
| + w_param |= ((GetKeyState(VK_CONTROL) & 0x80) == 0x80)? MK_CONTROL : 0;
|
| + w_param |= ((GetKeyState(VK_SHIFT) & 0x80) == 0x80)? MK_SHIFT : 0;
|
| + }
|
| + }
|
| + } else if (message == WM_NCRBUTTONDOWN &&
|
| + (w_param == HTCAPTION || w_param == HTSYSMENU)) {
|
| + is_right_mouse_pressed_on_caption_ = true;
|
| + // We SetMouseCapture() to ensure we only show the menu when the button
|
| + // down and up are both on the caption. Note: this causes the button up to
|
| + // be WM_RBUTTONUP instead of WM_NCRBUTTONUP.
|
| + SetMouseCapture();
|
| + }
|
| +
|
| + MSG msg = { hwnd(), message, w_param, l_param, 0,
|
| + { GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param) } };
|
| + MouseEvent event(msg);
|
| +
|
| + if (!(event.flags() & ui::EF_IS_NON_CLIENT))
|
| + if (tooltip_manager_.get())
|
| + tooltip_manager_->OnMouse(message, w_param, l_param);
|
| +
|
| + if (event.type() == ui::ET_MOUSE_MOVED && !HasMouseCapture()) {
|
| + // 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.
|
| + TrackMouseEvents((message == WM_NCMOUSEMOVE) ?
|
| + TME_NONCLIENT | TME_LEAVE : TME_LEAVE);
|
| + } else if (event.type() == ui::ET_MOUSE_EXITED) {
|
| + // Reset our tracking flags so future mouse movement over this
|
| + // NativeWidgetWin results in a new tracking session. Fall through for
|
| + // OnMouseEvent.
|
| + active_mouse_tracking_flags_ = 0;
|
| + } else if (event.type() == ui::ET_MOUSEWHEEL) {
|
| + // Reroute the mouse wheel to the window under the pointer if applicable.
|
| + return (ui::RerouteMouseWheel(hwnd(), w_param, l_param) ||
|
| + delegate_->OnMouseEvent(MouseWheelEvent(msg))) ? 0 : 1;
|
| + }
|
| +
|
| + bool handled = delegate_->OnMouseEvent(event);
|
| +
|
| + if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU &&
|
| + !GetWidget()->ShouldUseNativeFrame()) {
|
| + // TODO(msw): Eliminate undesired painting, or re-evaluate this workaround.
|
| + // DefWindowProc for WM_NCLBUTTONDOWN does weird non-client painting, so we
|
| + // need to call it inside a ScopedRedrawLock. This may cause other negative
|
| + // side-effects (ex/ stifling non-client mouse releases).
|
| + DefWindowProcWithRedrawLock(message, w_param, l_param);
|
| + handled = true;
|
| + }
|
| +
|
| + SetMsgHandled(handled);
|
| + return 0;
|
| +}
|
| +
|
| +void NativeWidgetWin::OnMove(const CPoint& point) {
|
| + // TODO(beng): move to Widget.
|
| + GetWidget()->widget_delegate()->OnWidgetMove();
|
| + SetMsgHandled(FALSE);
|
| +}
|
| +
|
| +void NativeWidgetWin::OnMoving(UINT param, const LPRECT new_bounds) {
|
| + // TODO(beng): move to Widget.
|
| + GetWidget()->widget_delegate()->OnWidgetMove();
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnNCActivate(BOOL active) {
|
| + if (delegate_->CanActivate())
|
| + delegate_->OnNativeWidgetActivationChanged(!!active);
|
| +
|
| + if (!GetWidget()->non_client_view()) {
|
| + SetMsgHandled(FALSE);
|
| + return 0;
|
| + }
|
| +
|
| + if (!delegate_->CanActivate())
|
| + return TRUE;
|
| +
|
| + // The frame may need to redraw as a result of the activation change.
|
| + // We can get WM_NCACTIVATE before we're actually visible. If we're not
|
| + // visible, no need to paint.
|
| + if (IsVisible())
|
| + GetWidget()->non_client_view()->SchedulePaint();
|
| +
|
| + if (!GetWidget()->ShouldUseNativeFrame()) {
|
| + // TODO(beng, et al): Hack to redraw this window and child windows
|
| + // synchronously upon activation. Not all child windows are redrawing
|
| + // themselves leading to issues like http://crbug.com/74604
|
| + // We redraw out-of-process HWNDs asynchronously to avoid hanging the
|
| + // whole app if a child HWND belonging to a hung plugin is encountered.
|
| + RedrawWindow(GetNativeView(), NULL, NULL,
|
| + RDW_NOCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW);
|
| + EnumChildWindows(GetNativeView(), EnumChildWindowsForRedraw, NULL);
|
| + }
|
| +
|
| + // If we're active again, we should be allowed to render as inactive, so
|
| + // tell the non-client view.
|
| + bool inactive_rendering_disabled = delegate_->IsInactiveRenderingDisabled();
|
| + if (IsActive())
|
| + delegate_->EnableInactiveRendering();
|
| +
|
| + // Avoid DefWindowProc non-client rendering over our custom frame.
|
| + if (!GetWidget()->ShouldUseNativeFrame()) {
|
| + SetMsgHandled(TRUE);
|
| + return TRUE;
|
| + }
|
| +
|
| + return DefWindowProcWithRedrawLock(WM_NCACTIVATE,
|
| + inactive_rendering_disabled || active, 0);
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnNCCalcSize(BOOL mode, LPARAM l_param) {
|
| + // We only override the default handling if we need to specify a custom
|
| + // non-client edge width. Note that in most cases "no insets" means no
|
| + // custom width, but in fullscreen mode we want a custom width of 0.
|
| + gfx::Insets insets = GetClientAreaInsets();
|
| + if (insets.empty() && !IsFullscreen()) {
|
| + SetMsgHandled(FALSE);
|
| + return 0;
|
| + }
|
| +
|
| + RECT* client_rect = mode ?
|
| + &(reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param)->rgrc[0]) :
|
| + reinterpret_cast<RECT*>(l_param);
|
| + client_rect->left += insets.left();
|
| + client_rect->top += insets.top();
|
| + client_rect->bottom -= insets.bottom();
|
| + client_rect->right -= insets.right();
|
| + if (IsMaximized()) {
|
| + // Find all auto-hide taskbars along the screen edges and adjust in by the
|
| + // thickness of the auto-hide taskbar on each such edge, so the window isn't
|
| + // treated as a "fullscreen app", which would cause the taskbars to
|
| + // disappear.
|
| + HMONITOR monitor = MonitorFromWindow(GetNativeView(),
|
| + MONITOR_DEFAULTTONULL);
|
| + if (!monitor) {
|
| + // We might end up here if the window was previously minimized and the
|
| + // user clicks on the taskbar button to restore it in the previously
|
| + // maximized position. In that case WM_NCCALCSIZE is sent before the
|
| + // window coordinates are restored to their previous values, so our
|
| + // (left,top) would probably be (-32000,-32000) like all minimized
|
| + // windows. So the above MonitorFromWindow call fails, but if we check
|
| + // the window rect given with WM_NCCALCSIZE (which is our previous
|
| + // restored window position) we will get the correct monitor handle.
|
| + monitor = MonitorFromRect(client_rect, MONITOR_DEFAULTTONULL);
|
| + if (!monitor) {
|
| + // This is probably an extreme case that we won't hit, but if we don't
|
| + // intersect any monitor, let us not adjust the client rect since our
|
| + // window will not be visible anyway.
|
| + return 0;
|
| + }
|
| + }
|
| + if (GetTopmostAutoHideTaskbarForEdge(ABE_LEFT, monitor))
|
| + client_rect->left += kAutoHideTaskbarThicknessPx;
|
| + if (GetTopmostAutoHideTaskbarForEdge(ABE_TOP, monitor)) {
|
| + if (GetWidget()->ShouldUseNativeFrame()) {
|
| + // Tricky bit. Due to a bug in DwmDefWindowProc()'s handling of
|
| + // WM_NCHITTEST, having any nonclient area atop the window causes the
|
| + // caption buttons to draw onscreen but not respond to mouse
|
| + // hover/clicks.
|
| + // So for a taskbar at the screen top, we can't push the
|
| + // client_rect->top down; instead, we move the bottom up by one pixel,
|
| + // which is the smallest change we can make and still get a client area
|
| + // less than the screen size. This is visibly ugly, but there seems to
|
| + // be no better solution.
|
| + --client_rect->bottom;
|
| + } else {
|
| + client_rect->top += kAutoHideTaskbarThicknessPx;
|
| + }
|
| + }
|
| + if (GetTopmostAutoHideTaskbarForEdge(ABE_RIGHT, monitor))
|
| + client_rect->right -= kAutoHideTaskbarThicknessPx;
|
| + if (GetTopmostAutoHideTaskbarForEdge(ABE_BOTTOM, monitor))
|
| + client_rect->bottom -= kAutoHideTaskbarThicknessPx;
|
| +
|
| + // We cannot return WVR_REDRAW when there is nonclient area, or Windows
|
| + // exhibits bugs where client pixels and child HWNDs are mispositioned by
|
| + // the width/height of the upper-left nonclient area.
|
| + return 0;
|
| + }
|
| +
|
| + // If the window bounds change, we're going to relayout and repaint anyway.
|
| + // Returning WVR_REDRAW avoids an extra paint before that of the old client
|
| + // pixels in the (now wrong) location, and thus makes actions like resizing a
|
| + // window from the left edge look slightly less broken.
|
| + // We special case when left or top insets are 0, since these conditions
|
| + // actually require another repaint to correct the layout after glass gets
|
| + // turned on and off.
|
| + if (insets.left() == 0 || insets.top() == 0)
|
| + return 0;
|
| + return mode ? WVR_REDRAW : 0;
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnNCHitTest(const CPoint& point) {
|
| + if (!GetWidget()->non_client_view()) {
|
| + SetMsgHandled(FALSE);
|
| + return 0;
|
| + }
|
| +
|
| + // If the DWM is rendering the window controls, we need to give the DWM's
|
| + // default window procedure first chance to handle hit testing.
|
| + if (GetWidget()->ShouldUseNativeFrame()) {
|
| + LRESULT result;
|
| + if (DwmDefWindowProc(GetNativeView(), WM_NCHITTEST, 0,
|
| + MAKELPARAM(point.x, point.y), &result)) {
|
| + return result;
|
| + }
|
| + }
|
| +
|
| + // First, give the NonClientView a chance to test the point to see if it
|
| + // provides any of the non-client area.
|
| + POINT temp = point;
|
| + MapWindowPoints(HWND_DESKTOP, GetNativeView(), &temp, 1);
|
| + int component = delegate_->GetNonClientComponent(gfx::Point(temp));
|
| + if (component != HTNOWHERE)
|
| + return component;
|
| +
|
| + // Otherwise, we let Windows do all the native frame non-client handling for
|
| + // us.
|
| + SetMsgHandled(FALSE);
|
| + return 0;
|
| +}
|
| +
|
| +void NativeWidgetWin::OnNCPaint(HRGN rgn) {
|
| + // We only do non-client painting if we're not using the native frame.
|
| + // It's required to avoid some native painting artifacts from appearing when
|
| + // the window is resized.
|
| + if (!GetWidget()->non_client_view() || GetWidget()->ShouldUseNativeFrame()) {
|
| + SetMsgHandled(FALSE);
|
| + return;
|
| + }
|
| +
|
| + // We have an NC region and need to paint it. We expand the NC region to
|
| + // include the dirty region of the root view. This is done to minimize
|
| + // paints.
|
| + CRect window_rect;
|
| + GetWindowRect(&window_rect);
|
| +
|
| + if (window_rect.Width() != GetWidget()->GetRootView()->width() ||
|
| + window_rect.Height() != GetWidget()->GetRootView()->height()) {
|
| + // If the size of the window differs from the size of the root view it
|
| + // means we're being asked to paint before we've gotten a WM_SIZE. This can
|
| + // happen when the user is interactively resizing the window. To avoid
|
| + // mass flickering we don't do anything here. Once we get the WM_SIZE we'll
|
| + // reset the region of the window which triggers another WM_NCPAINT and
|
| + // all is well.
|
| + return;
|
| + }
|
| +
|
| + CRect dirty_region;
|
| + // A value of 1 indicates paint all.
|
| + if (!rgn || rgn == reinterpret_cast<HRGN>(1)) {
|
| + dirty_region = CRect(0, 0, window_rect.Width(), window_rect.Height());
|
| + } else {
|
| + RECT rgn_bounding_box;
|
| + GetRgnBox(rgn, &rgn_bounding_box);
|
| + if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect))
|
| + return; // Dirty region doesn't intersect window bounds, bale.
|
| +
|
| + // rgn_bounding_box is in screen coordinates. Map it to window coordinates.
|
| + OffsetRect(&dirty_region, -window_rect.left, -window_rect.top);
|
| + }
|
| +
|
| + // In theory GetDCEx should do what we want, but I couldn't get it to work.
|
| + // In particular the docs mentiond DCX_CLIPCHILDREN, but as far as I can tell
|
| + // it doesn't work at all. So, instead we get the DC for the window then
|
| + // manually clip out the children.
|
| + HDC dc = GetWindowDC(GetNativeView());
|
| + ClipState clip_state;
|
| + clip_state.x = window_rect.left;
|
| + clip_state.y = window_rect.top;
|
| + clip_state.parent = GetNativeView();
|
| + clip_state.dc = dc;
|
| + EnumChildWindows(GetNativeView(), &ClipDCToChild,
|
| + reinterpret_cast<LPARAM>(&clip_state));
|
| +
|
| + gfx::Rect old_paint_region = invalid_rect();
|
| +
|
| + if (!old_paint_region.IsEmpty()) {
|
| + // The root view has a region that needs to be painted. Include it in the
|
| + // region we're going to paint.
|
| +
|
| + CRect old_paint_region_crect = old_paint_region.ToRECT();
|
| + CRect tmp = dirty_region;
|
| + UnionRect(&dirty_region, &tmp, &old_paint_region_crect);
|
| + }
|
| +
|
| + GetWidget()->GetRootView()->SchedulePaintInRect(gfx::Rect(dirty_region));
|
| +
|
| + // gfx::CanvasSkiaPaint's destructor does the actual painting. As such, wrap
|
| + // the following in a block to force paint to occur so that we can release
|
| + // the dc.
|
| + {
|
| + gfx::CanvasSkiaPaint canvas(dc, true, dirty_region.left,
|
| + dirty_region.top, dirty_region.Width(),
|
| + dirty_region.Height());
|
| + delegate_->OnNativeWidgetPaint(&canvas);
|
| + }
|
| +
|
| + ReleaseDC(GetNativeView(), dc);
|
| + // When using a custom frame, we want to avoid calling DefWindowProc() since
|
| + // that may render artifacts.
|
| + SetMsgHandled(!GetWidget()->ShouldUseNativeFrame());
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnNCUAHDrawCaption(UINT msg,
|
| + WPARAM w_param,
|
| + LPARAM l_param) {
|
| + // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for
|
| + // an explanation about why we need to handle this message.
|
| + SetMsgHandled(!GetWidget()->ShouldUseNativeFrame());
|
| + return 0;
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnNCUAHDrawFrame(UINT msg,
|
| + WPARAM w_param,
|
| + LPARAM l_param) {
|
| + // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for
|
| + // an explanation about why we need to handle this message.
|
| + SetMsgHandled(!GetWidget()->ShouldUseNativeFrame());
|
| + return 0;
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnNotify(int w_param, NMHDR* l_param) {
|
| + // We can be sent this message before the tooltip manager is created, if a
|
| + // subclass overrides OnCreate and creates some kind of Windows control there
|
| + // that sends WM_NOTIFY messages.
|
| + if (tooltip_manager_.get()) {
|
| + bool handled;
|
| + LRESULT result = tooltip_manager_->OnNotify(w_param, l_param, &handled);
|
| + SetMsgHandled(handled);
|
| + return result;
|
| + }
|
| + SetMsgHandled(FALSE);
|
| + return 0;
|
| +}
|
| +
|
| +void NativeWidgetWin::OnPaint(HDC dc) {
|
| + RECT dirty_rect;
|
| + // Try to paint accelerated first.
|
| + if (GetUpdateRect(hwnd(), &dirty_rect, FALSE) &&
|
| + !IsRectEmpty(&dirty_rect)) {
|
| + if (delegate_->OnNativeWidgetPaintAccelerated(
|
| + gfx::Rect(dirty_rect))) {
|
| + ValidateRect(hwnd(), NULL);
|
| + } else {
|
| + scoped_ptr<gfx::CanvasPaint> canvas(
|
| + gfx::CanvasPaint::CreateCanvasPaint(hwnd()));
|
| + delegate_->OnNativeWidgetPaint(canvas->AsCanvas());
|
| + }
|
| + } else {
|
| + // TODO(msw): Find a better solution for this crbug.com/93530 workaround.
|
| + // Some scenarios otherwise fail to validate minimized app/popup windows.
|
| + ValidateRect(hwnd(), NULL);
|
| + }
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnPowerBroadcast(DWORD power_event, DWORD data) {
|
| + base::SystemMonitor* monitor = base::SystemMonitor::Get();
|
| + if (monitor)
|
| + monitor->ProcessWmPowerBroadcastMessage(power_event);
|
| + SetMsgHandled(FALSE);
|
| + return 0;
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnReflectedMessage(UINT msg,
|
| + WPARAM w_param,
|
| + LPARAM l_param) {
|
| + SetMsgHandled(FALSE);
|
| + return 0;
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnSetCursor(UINT message,
|
| + WPARAM w_param,
|
| + LPARAM l_param) {
|
| + // Using ScopedRedrawLock here frequently allows content behind this window to
|
| + // paint in front of this window, causing glaring rendering artifacts.
|
| + // If omitting ScopedRedrawLock here triggers caption rendering artifacts via
|
| + // DefWindowProc message handling, we'll need to find a better solution.
|
| + SetMsgHandled(FALSE);
|
| + return 0;
|
| +}
|
| +
|
| +void NativeWidgetWin::OnSetFocus(HWND focused_window) {
|
| + delegate_->OnNativeFocus(focused_window);
|
| + InputMethod* input_method = GetWidget()->GetInputMethodDirect();
|
| + if (input_method)
|
| + input_method->OnFocus();
|
| + SetMsgHandled(FALSE);
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnSetIcon(UINT size_type, HICON new_icon) {
|
| + // This shouldn't hurt even if we're using the native frame.
|
| + return DefWindowProcWithRedrawLock(WM_SETICON, size_type,
|
| + reinterpret_cast<LPARAM>(new_icon));
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::OnSetText(const wchar_t* text) {
|
| + // This shouldn't hurt even if we're using the native frame.
|
| + return DefWindowProcWithRedrawLock(WM_SETTEXT, NULL,
|
| + reinterpret_cast<LPARAM>(text));
|
| +}
|
| +
|
| +void NativeWidgetWin::OnSettingChange(UINT flags, const wchar_t* section) {
|
| + if (!GetParent() && (flags == SPI_SETWORKAREA) &&
|
| + !GetWidget()->widget_delegate()->WillProcessWorkAreaChange()) {
|
| + // Fire a dummy SetWindowPos() call, so we'll trip the code in
|
| + // OnWindowPosChanging() below that notices work area changes.
|
| + ::SetWindowPos(GetNativeView(), 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
|
| + SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
|
| + SetMsgHandled(TRUE);
|
| + } else {
|
| + // TODO(beng): move to Widget.
|
| + if (flags == SPI_SETWORKAREA)
|
| + GetWidget()->widget_delegate()->OnWorkAreaChanged();
|
| + SetMsgHandled(FALSE);
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::OnSize(UINT param, const CSize& size) {
|
| + RedrawWindow(GetNativeView(), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
|
| + // ResetWindowRegion is going to trigger WM_NCPAINT. By doing it after we've
|
| + // invoked OnSize we ensure the RootView has been laid out.
|
| + ResetWindowRegion(false);
|
| +}
|
| +
|
| +void NativeWidgetWin::OnSysCommand(UINT notification_code, CPoint click) {
|
| + if (!GetWidget()->non_client_view())
|
| + return;
|
| +
|
| + // Windows uses the 4 lower order bits of |notification_code| for type-
|
| + // specific information so we must exclude this when comparing.
|
| + static const int sc_mask = 0xFFF0;
|
| + // Ignore size/move/maximize in fullscreen mode.
|
| + if (IsFullscreen() &&
|
| + (((notification_code & sc_mask) == SC_SIZE) ||
|
| + ((notification_code & sc_mask) == SC_MOVE) ||
|
| + ((notification_code & sc_mask) == SC_MAXIMIZE)))
|
| + return;
|
| + if (!GetWidget()->ShouldUseNativeFrame()) {
|
| + if ((notification_code & sc_mask) == SC_MINIMIZE ||
|
| + (notification_code & sc_mask) == SC_MAXIMIZE ||
|
| + (notification_code & sc_mask) == SC_RESTORE) {
|
| + GetWidget()->non_client_view()->ResetWindowControls();
|
| + } else if ((notification_code & sc_mask) == SC_MOVE ||
|
| + (notification_code & sc_mask) == SC_SIZE) {
|
| + if (!IsVisible()) {
|
| + // Circumvent ScopedRedrawLocks and force visibility before entering a
|
| + // resize or move modal loop to get continuous sizing/moving feedback.
|
| + SetWindowLong(GWL_STYLE, GetWindowLong(GWL_STYLE) | WS_VISIBLE);
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Handle SC_KEYMENU, which means that the user has pressed the ALT
|
| + // key and released it, so we should focus the menu bar.
|
| + if ((notification_code & sc_mask) == SC_KEYMENU && click.x == 0) {
|
| + // Retrieve the status of shift and control keys to prevent consuming
|
| + // shift+alt keys, which are used by Windows to change input languages.
|
| + ui::Accelerator accelerator(ui::KeyboardCodeForWindowsKeyCode(VK_MENU),
|
| + !!(GetKeyState(VK_SHIFT) & 0x8000),
|
| + !!(GetKeyState(VK_CONTROL) & 0x8000),
|
| + false);
|
| + GetWidget()->GetFocusManager()->ProcessAccelerator(accelerator);
|
| + return;
|
| + }
|
| +
|
| + // If the delegate can't handle it, the system implementation will be called.
|
| + if (!delegate_->ExecuteCommand(notification_code)) {
|
| + DefWindowProc(GetNativeView(), WM_SYSCOMMAND, notification_code,
|
| + MAKELPARAM(click.x, click.y));
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::OnThemeChanged() {
|
| + // Notify NativeThemeWin.
|
| + gfx::NativeThemeWin::instance()->CloseHandles();
|
| +}
|
| +
|
| +void NativeWidgetWin::OnVScroll(int scroll_type,
|
| + short position,
|
| + HWND scrollbar) {
|
| + SetMsgHandled(FALSE);
|
| +}
|
| +
|
| +void NativeWidgetWin::OnWindowPosChanging(WINDOWPOS* window_pos) {
|
| + if (ignore_window_pos_changes_) {
|
| + // If somebody's trying to toggle our visibility, change the nonclient area,
|
| + // change our Z-order, or activate us, we should probably let it go through.
|
| + if (!(window_pos->flags & ((IsVisible() ? SWP_HIDEWINDOW : SWP_SHOWWINDOW) |
|
| + SWP_FRAMECHANGED)) &&
|
| + (window_pos->flags & (SWP_NOZORDER | SWP_NOACTIVATE))) {
|
| + // Just sizing/moving the window; ignore.
|
| + window_pos->flags |= SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW;
|
| + window_pos->flags &= ~(SWP_SHOWWINDOW | SWP_HIDEWINDOW);
|
| + }
|
| + } else if (!GetParent()) {
|
| + CRect window_rect;
|
| + HMONITOR monitor;
|
| + gfx::Rect monitor_rect, work_area;
|
| + if (GetWindowRect(&window_rect) &&
|
| + GetMonitorAndRects(window_rect, &monitor, &monitor_rect, &work_area)) {
|
| + if (monitor && (monitor == last_monitor_) &&
|
| + (IsFullscreen() || ((monitor_rect == last_monitor_rect_) &&
|
| + (work_area != last_work_area_)))) {
|
| + // A rect for the monitor we're on changed. Normally Windows notifies
|
| + // us about this (and thus we're reaching here due to the SetWindowPos()
|
| + // call in OnSettingChange() above), but with some software (e.g.
|
| + // nVidia's nView desktop manager) the work area can change asynchronous
|
| + // to any notification, and we're just sent a SetWindowPos() call with a
|
| + // new (frequently incorrect) position/size. In either case, the best
|
| + // response is to throw away the existing position/size information in
|
| + // |window_pos| and recalculate it based on the new work rect.
|
| + gfx::Rect new_window_rect;
|
| + if (IsFullscreen()) {
|
| + new_window_rect = monitor_rect;
|
| + } else if (IsZoomed()) {
|
| + new_window_rect = work_area;
|
| + int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
|
| + new_window_rect.Inset(-border_thickness, -border_thickness);
|
| + } else {
|
| + new_window_rect = gfx::Rect(window_rect).AdjustToFit(work_area);
|
| + }
|
| + window_pos->x = new_window_rect.x();
|
| + window_pos->y = new_window_rect.y();
|
| + window_pos->cx = new_window_rect.width();
|
| + window_pos->cy = new_window_rect.height();
|
| + // WARNING! Don't set SWP_FRAMECHANGED here, it breaks moving the child
|
| + // HWNDs for some reason.
|
| + window_pos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW);
|
| + window_pos->flags |= SWP_NOCOPYBITS;
|
| +
|
| + // Now ignore all immediately-following SetWindowPos() changes. Windows
|
| + // likes to (incorrectly) recalculate what our position/size should be
|
| + // and send us further updates.
|
| + ignore_window_pos_changes_ = true;
|
| + DCHECK(!ignore_pos_changes_factory_.HasWeakPtrs());
|
| + MessageLoop::current()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&NativeWidgetWin::StopIgnoringPosChanges,
|
| + ignore_pos_changes_factory_.GetWeakPtr()));
|
| + }
|
| + last_monitor_ = monitor;
|
| + last_monitor_rect_ = monitor_rect;
|
| + last_work_area_ = work_area;
|
| + }
|
| + }
|
| +
|
| + if (force_hidden_count_) {
|
| + // Prevent the window from being made visible if we've been asked to do so.
|
| + // See comment in header as to why we might want this.
|
| + window_pos->flags &= ~SWP_SHOWWINDOW;
|
| + }
|
| +
|
| + // When WM_WINDOWPOSCHANGING message is handled by DefWindowProc, it will
|
| + // enforce (cx, cy) not to be smaller than (6, 6) for any non-popup window.
|
| + // We work around this by changing cy back to our intended value.
|
| + if (!GetParent() && ~(window_pos->flags & SWP_NOSIZE) && window_pos->cy < 6) {
|
| + LONG old_cy = window_pos->cy;
|
| + DefWindowProc(GetNativeView(), WM_WINDOWPOSCHANGING, 0,
|
| + reinterpret_cast<LPARAM>(window_pos));
|
| + window_pos->cy = old_cy;
|
| + SetMsgHandled(TRUE);
|
| + return;
|
| + }
|
| +
|
| + SetMsgHandled(FALSE);
|
| +}
|
| +
|
| +void NativeWidgetWin::OnWindowPosChanged(WINDOWPOS* window_pos) {
|
| + if (DidClientAreaSizeChange(window_pos))
|
| + ClientAreaSizeChanged();
|
| + if (window_pos->flags & SWP_SHOWWINDOW)
|
| + delegate_->OnNativeWidgetVisibilityChanged(true);
|
| + else if (window_pos->flags & SWP_HIDEWINDOW)
|
| + delegate_->OnNativeWidgetVisibilityChanged(false);
|
| + SetMsgHandled(FALSE);
|
| +}
|
| +
|
| +void NativeWidgetWin::OnFinalMessage(HWND window) {
|
| + // We don't destroy props in WM_DESTROY as we may still get messages after
|
| + // WM_DESTROY that assume the properties are still valid (such as WM_CLOSE).
|
| + props_.reset();
|
| + delegate_->OnNativeWidgetDestroyed();
|
| + if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
|
| + delete this;
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// NativeWidgetWin, protected:
|
| +
|
| +int NativeWidgetWin::GetShowState() const {
|
| + return SW_SHOWNORMAL;
|
| +}
|
| +
|
| +gfx::Insets NativeWidgetWin::GetClientAreaInsets() const {
|
| + // Returning an empty Insets object causes the default handling in
|
| + // NativeWidgetWin::OnNCCalcSize() to be invoked.
|
| + if (!has_non_client_view_ || GetWidget()->ShouldUseNativeFrame())
|
| + return gfx::Insets();
|
| +
|
| + if (IsMaximized()) {
|
| + // Windows automatically adds a standard width border to all sides when a
|
| + // window is maximized.
|
| + int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
|
| + return gfx::Insets(border_thickness, border_thickness, border_thickness,
|
| + border_thickness);
|
| + }
|
| + // This is weird, but highly essential. If we don't offset the bottom edge
|
| + // of the client rect, the window client area and window area will match,
|
| + // and when returning to glass rendering mode from non-glass, the client
|
| + // area will not paint black as transparent. This is because (and I don't
|
| + // know why) the client area goes from matching the window rect to being
|
| + // something else. If the client area is not the window rect in both
|
| + // modes, the blackness doesn't occur. Because of this, we need to tell
|
| + // the RootView to lay out to fit the window rect, rather than the client
|
| + // rect when using the opaque frame.
|
| + // Note: this is only required for non-fullscreen windows. Note that
|
| + // fullscreen windows are in restored state, not maximized.
|
| + return gfx::Insets(0, 0, IsFullscreen() ? 0 : 1, 0);
|
| +}
|
| +
|
| +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);
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::OnScreenReaderDetected() {
|
| + screen_reader_active_ = true;
|
| +}
|
| +
|
| +void NativeWidgetWin::SetInitialFocus() {
|
| + if (!GetWidget()->SetInitialFocus() &&
|
| + !(GetWindowLong(GWL_EXSTYLE) & WS_EX_TRANSPARENT) &&
|
| + !(GetWindowLong(GWL_EXSTYLE) & WS_EX_NOACTIVATE)) {
|
| + // The window does not get keyboard messages unless we focus it.
|
| + SetFocus(GetNativeView());
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::ExecuteSystemMenuCommand(int command) {
|
| + if (command)
|
| + SendMessage(GetNativeView(), WM_SYSCOMMAND, command, 0);
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// NativeWidgetWin, private:
|
| +
|
| +// static
|
| +void NativeWidgetWin::PostProcessActivateMessage(NativeWidgetWin* widget,
|
| + int activation_state) {
|
| + DCHECK(widget->GetWidget()->is_top_level());
|
| + FocusManager* focus_manager = widget->GetWidget()->GetFocusManager();
|
| + if (WA_INACTIVE == activation_state) {
|
| + // We might get activated/inactivated without being enabled, so we need to
|
| + // clear restore_focus_when_enabled_.
|
| + widget->restore_focus_when_enabled_ = false;
|
| + focus_manager->StoreFocusedView();
|
| + } else {
|
| + // We must restore the focus after the message has been DefProc'ed as it
|
| + // does set the focus to the last focused HWND.
|
| + // Note that if the window is not enabled, we cannot restore the focus as
|
| + // calling ::SetFocus on a child of the non-enabled top-window would fail.
|
| + // This is the case when showing a modal dialog (such as 'open file',
|
| + // 'print'...) from a different thread.
|
| + // In that case we delay the focus restoration to when the window is enabled
|
| + // again.
|
| + if (!IsWindowEnabled(widget->GetNativeView())) {
|
| + DCHECK(!widget->restore_focus_when_enabled_);
|
| + widget->restore_focus_when_enabled_ = true;
|
| + return;
|
| + }
|
| + focus_manager->RestoreFocusedView();
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::SetInitParams(const Widget::InitParams& params) {
|
| + // Set non-style attributes.
|
| + ownership_ = params.ownership;
|
| +
|
| + DWORD style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
|
| + DWORD ex_style = 0;
|
| + DWORD class_style = CS_DBLCLKS;
|
| +
|
| + // Set type-independent style attributes.
|
| + if (params.child)
|
| + style |= WS_CHILD;
|
| + if (params.show_state == ui::SHOW_STATE_MAXIMIZED)
|
| + style |= WS_MAXIMIZE;
|
| + if (params.show_state == ui::SHOW_STATE_MINIMIZED)
|
| + style |= WS_MINIMIZE;
|
| + if (!params.accept_events)
|
| + ex_style |= WS_EX_TRANSPARENT;
|
| + if (!params.can_activate)
|
| + ex_style |= WS_EX_NOACTIVATE;
|
| + if (params.keep_on_top)
|
| + ex_style |= WS_EX_TOPMOST;
|
| + if (params.mirror_origin_in_rtl)
|
| + ex_style |= l10n_util::GetExtendedTooltipStyles();
|
| + if (params.transparent)
|
| + ex_style |= WS_EX_LAYERED;
|
| + if (params.has_dropshadow) {
|
| + class_style |= (base::win::GetVersion() < base::win::VERSION_XP) ?
|
| + 0 : CS_DROPSHADOW;
|
| + }
|
| +
|
| + // Set type-dependent style attributes.
|
| + switch (params.type) {
|
| + case Widget::InitParams::TYPE_WINDOW: {
|
| + style |= WS_SYSMENU | WS_CAPTION;
|
| + bool can_resize = GetWidget()->widget_delegate()->CanResize();
|
| + bool can_maximize = GetWidget()->widget_delegate()->CanMaximize();
|
| + if (can_maximize) {
|
| + style |= WS_OVERLAPPEDWINDOW;
|
| + } else if (can_resize) {
|
| + style |= WS_OVERLAPPED | WS_THICKFRAME;
|
| + }
|
| + if (delegate_->IsDialogBox()) {
|
| + style |= DS_MODALFRAME;
|
| + // NOTE: Turning this off means we lose the close button, which is bad.
|
| + // Turning it on though means the user can maximize or size the window
|
| + // from the system menu, which is worse. We may need to provide our own
|
| + // menu to get the close button to appear properly.
|
| + // style &= ~WS_SYSMENU;
|
| +
|
| + // Set the WS_POPUP style for modal dialogs. This ensures that the owner
|
| + // window is activated on destruction. This style should not be set for
|
| + // non-modal non-top-level dialogs like constrained windows.
|
| + style |= delegate_->IsModal() ? WS_POPUP : 0;
|
| + }
|
| + ex_style |= delegate_->IsDialogBox() ? WS_EX_DLGMODALFRAME : 0;
|
| + break;
|
| + }
|
| + case Widget::InitParams::TYPE_CONTROL:
|
| + style |= WS_VISIBLE;
|
| + break;
|
| + case Widget::InitParams::TYPE_WINDOW_FRAMELESS:
|
| + style |= WS_POPUP;
|
| + break;
|
| + case Widget::InitParams::TYPE_BUBBLE:
|
| + style |= WS_POPUP;
|
| + style |= WS_CLIPCHILDREN;
|
| + break;
|
| + case Widget::InitParams::TYPE_POPUP:
|
| + style |= WS_POPUP;
|
| + ex_style |= WS_EX_TOOLWINDOW;
|
| + break;
|
| + case Widget::InitParams::TYPE_MENU:
|
| + style |= WS_POPUP;
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + }
|
| +
|
| + set_initial_class_style(class_style);
|
| + set_window_style(window_style() | style);
|
| + set_window_ex_style(window_ex_style() | ex_style);
|
| +
|
| + has_non_client_view_ = Widget::RequiresNonClientView(params.type);
|
| +}
|
| +
|
| +void NativeWidgetWin::RedrawInvalidRect() {
|
| + if (!use_layered_buffer_) {
|
| + RECT r = { 0, 0, 0, 0 };
|
| + if (GetUpdateRect(hwnd(), &r, FALSE) && !IsRectEmpty(&r)) {
|
| + RedrawWindow(hwnd(), &r, NULL,
|
| + RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::RedrawLayeredWindowContents() {
|
| + if (invalid_rect_.IsEmpty())
|
| + return;
|
| +
|
| + // We need to clip to the dirty rect ourselves.
|
| + layered_window_contents_->sk_canvas()->save(SkCanvas::kClip_SaveFlag);
|
| + layered_window_contents_->ClipRect(invalid_rect_);
|
| + GetWidget()->GetRootView()->Paint(layered_window_contents_.get());
|
| + layered_window_contents_->sk_canvas()->restore();
|
| +
|
| + RECT wr;
|
| + GetWindowRect(&wr);
|
| + SIZE size = {wr.right - wr.left, wr.bottom - wr.top};
|
| + POINT position = {wr.left, wr.top};
|
| + HDC dib_dc = skia::BeginPlatformPaint(layered_window_contents_->sk_canvas());
|
| + POINT zero = {0, 0};
|
| + BLENDFUNCTION blend = {AC_SRC_OVER, 0, layered_alpha_, AC_SRC_ALPHA};
|
| + UpdateLayeredWindow(hwnd(), NULL, &position, &size, dib_dc, &zero,
|
| + RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA);
|
| + invalid_rect_.SetRect(0, 0, 0, 0);
|
| + skia::EndPlatformPaint(layered_window_contents_->sk_canvas());
|
| +}
|
| +
|
| +void NativeWidgetWin::LockUpdates() {
|
| + // We skip locked updates when Aero is on for two reasons:
|
| + // 1. Because it isn't necessary
|
| + // 2. Because toggling the WS_VISIBLE flag may occur while the GPU process is
|
| + // attempting to present a child window's backbuffer onscreen. When these
|
| + // two actions race with one another, the child window will either flicker
|
| + // or will simply stop updating entirely.
|
| + if (!IsAeroGlassEnabled() && ++lock_updates_count_ == 1) {
|
| + SetWindowLong(GWL_STYLE, GetWindowLong(GWL_STYLE) & ~WS_VISIBLE);
|
| + }
|
| + // TODO(msw): Remove nested LockUpdates VLOG info for crbug.com/93530.
|
| + VLOG_IF(1, (lock_updates_count_ > 1)) << "Nested LockUpdates call: "
|
| + << lock_updates_count_ << " locks for widget " << this;
|
| +}
|
| +
|
| +void NativeWidgetWin::UnlockUpdates() {
|
| + // TODO(msw): Remove nested LockUpdates VLOG info for crbug.com/93530.
|
| + VLOG_IF(1, (lock_updates_count_ > 1)) << "Nested UnlockUpdates call: "
|
| + << lock_updates_count_ << " locks for widget " << this;
|
| + if (!IsAeroGlassEnabled() && --lock_updates_count_ <= 0) {
|
| + SetWindowLong(GWL_STYLE, GetWindowLong(GWL_STYLE) | WS_VISIBLE);
|
| + lock_updates_count_ = 0;
|
| + }
|
| +}
|
| +
|
| +bool NativeWidgetWin::WidgetSizeIsClientSize() const {
|
| + const Widget* widget = GetWidget()->GetTopLevelWidget();
|
| + return IsZoomed() || (widget && widget->ShouldUseNativeFrame());
|
| +}
|
| +
|
| +void NativeWidgetWin::ClientAreaSizeChanged() {
|
| + RECT r;
|
| + if (WidgetSizeIsClientSize())
|
| + GetClientRect(&r);
|
| + else
|
| + GetWindowRect(&r);
|
| + gfx::Size s(std::max(0, static_cast<int>(r.right - r.left)),
|
| + std::max(0, static_cast<int>(r.bottom - r.top)));
|
| + if (compositor_.get())
|
| + compositor_->WidgetSizeChanged(s);
|
| + delegate_->OnNativeWidgetSizeChanged(s);
|
| + if (use_layered_buffer_) {
|
| + layered_window_contents_.reset(
|
| + new gfx::CanvasSkia(s.width(), s.height(), false));
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::ResetWindowRegion(bool force) {
|
| + // A native frame uses the native window region, and we don't want to mess
|
| + // with it.
|
| + if (GetWidget()->ShouldUseNativeFrame() || !GetWidget()->non_client_view()) {
|
| + if (force)
|
| + SetWindowRgn(NULL, TRUE);
|
| + return;
|
| + }
|
| +
|
| + // Changing the window region is going to force a paint. Only change the
|
| + // window region if the region really differs.
|
| + HRGN current_rgn = CreateRectRgn(0, 0, 0, 0);
|
| + int current_rgn_result = GetWindowRgn(GetNativeView(), current_rgn);
|
| +
|
| + CRect window_rect;
|
| + GetWindowRect(&window_rect);
|
| + HRGN new_region;
|
| + if (IsMaximized()) {
|
| + HMONITOR monitor =
|
| + MonitorFromWindow(GetNativeView(), MONITOR_DEFAULTTONEAREST);
|
| + MONITORINFO mi;
|
| + mi.cbSize = sizeof mi;
|
| + GetMonitorInfo(monitor, &mi);
|
| + CRect work_rect = mi.rcWork;
|
| + work_rect.OffsetRect(-window_rect.left, -window_rect.top);
|
| + new_region = CreateRectRgnIndirect(&work_rect);
|
| + } else {
|
| + gfx::Path window_mask;
|
| + GetWidget()->non_client_view()->GetWindowMask(
|
| + gfx::Size(window_rect.Width(), window_rect.Height()), &window_mask);
|
| + new_region = window_mask.CreateNativeRegion();
|
| + }
|
| +
|
| + if (current_rgn_result == ERROR || !EqualRgn(current_rgn, new_region)) {
|
| + // SetWindowRgn takes ownership of the HRGN created by CreateNativeRegion.
|
| + SetWindowRgn(new_region, TRUE);
|
| + } else {
|
| + DeleteObject(new_region);
|
| + }
|
| +
|
| + DeleteObject(current_rgn);
|
| +}
|
| +
|
| +LRESULT NativeWidgetWin::DefWindowProcWithRedrawLock(UINT message,
|
| + WPARAM w_param,
|
| + LPARAM l_param) {
|
| + ScopedRedrawLock lock(this);
|
| + // The Widget and HWND can be destroyed in the call to DefWindowProc, so use
|
| + // the |destroyed_| flag to avoid unlocking (and crashing) after destruction.
|
| + bool destroyed = false;
|
| + destroyed_ = &destroyed;
|
| + LRESULT result = DefWindowProc(GetNativeView(), message, w_param, l_param);
|
| + if (destroyed)
|
| + lock.CancelUnlockOperation();
|
| + else
|
| + destroyed_ = NULL;
|
| + return result;
|
| +}
|
| +
|
| +void NativeWidgetWin::RestoreEnabledIfNecessary() {
|
| + if (delegate_->IsModal() && !restored_enabled_) {
|
| + restored_enabled_ = true;
|
| + // If we were run modally, we need to undo the disabled-ness we inflicted on
|
| + // the owner's parent hierarchy.
|
| + HWND start = ::GetWindow(GetNativeView(), GW_OWNER);
|
| + while (start) {
|
| + ::EnableWindow(start, TRUE);
|
| + start = ::GetParent(start);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void NativeWidgetWin::DispatchKeyEventPostIME(const KeyEvent& key) {
|
| + SetMsgHandled(delegate_->OnKeyEvent(key));
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// Widget, public:
|
| +
|
| +// static
|
| +void Widget::NotifyLocaleChanged() {
|
| + NOTIMPLEMENTED();
|
| +}
|
| +
|
| +namespace {
|
| +BOOL CALLBACK WindowCallbackProc(HWND hwnd, LPARAM lParam) {
|
| + Widget* widget = Widget::GetWidgetForNativeView(hwnd);
|
| + if (widget && widget->is_secondary_widget())
|
| + widget->Close();
|
| + return TRUE;
|
| +}
|
| +} // namespace
|
| +
|
| +// static
|
| +void Widget::CloseAllSecondaryWidgets() {
|
| + EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, 0);
|
| +}
|
| +
|
| +bool Widget::ConvertRect(const Widget* source,
|
| + const Widget* target,
|
| + gfx::Rect* rect) {
|
| + DCHECK(source);
|
| + DCHECK(target);
|
| + DCHECK(rect);
|
| +
|
| + HWND source_hwnd = source->GetNativeView();
|
| + HWND target_hwnd = target->GetNativeView();
|
| + if (source_hwnd == target_hwnd)
|
| + return true;
|
| +
|
| + RECT win_rect = rect->ToRECT();
|
| + if (::MapWindowPoints(source_hwnd, target_hwnd,
|
| + reinterpret_cast<LPPOINT>(&win_rect),
|
| + sizeof(RECT)/sizeof(POINT))) {
|
| + *rect = win_rect;
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +namespace internal {
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// internal::NativeWidgetPrivate, public:
|
| +
|
| +// static
|
| +NativeWidgetPrivate* NativeWidgetPrivate::CreateNativeWidget(
|
| + internal::NativeWidgetDelegate* delegate) {
|
| + return new NativeWidgetWin(delegate);
|
| +}
|
| +
|
| +// static
|
| +NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView(
|
| + gfx::NativeView native_view) {
|
| + return reinterpret_cast<NativeWidgetWin*>(
|
| + ViewProp::GetValue(native_view, kNativeWidgetKey));
|
| +}
|
| +
|
| +// static
|
| +NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow(
|
| + gfx::NativeWindow native_window) {
|
| + return GetNativeWidgetForNativeView(native_window);
|
| +}
|
| +
|
| +// static
|
| +NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget(
|
| + gfx::NativeView native_view) {
|
| + if (!native_view)
|
| + return NULL;
|
| +
|
| + // First, check if the top-level window is a Widget.
|
| + HWND root = ::GetAncestor(native_view, GA_ROOT);
|
| + if (!root)
|
| + return NULL;
|
| +
|
| + NativeWidgetPrivate* widget = GetNativeWidgetForNativeView(root);
|
| + if (widget)
|
| + return widget;
|
| +
|
| + // Second, try to locate the last Widget window in the parent hierarchy.
|
| + HWND parent_hwnd = native_view;
|
| + // If we fail to find the native widget pointer for the root then it probably
|
| + // means that the root belongs to a different process in which case we walk up
|
| + // the native view chain looking for a parent window which corresponds to a
|
| + // valid native widget. We only do this if we fail to find the native widget
|
| + // for the current native view which means it is being destroyed.
|
| + if (!widget && !GetNativeWidgetForNativeView(native_view)) {
|
| + parent_hwnd = ::GetAncestor(parent_hwnd, GA_PARENT);
|
| + if (!parent_hwnd)
|
| + return NULL;
|
| + }
|
| + NativeWidgetPrivate* parent_widget;
|
| + do {
|
| + parent_widget = GetNativeWidgetForNativeView(parent_hwnd);
|
| + if (parent_widget) {
|
| + widget = parent_widget;
|
| + parent_hwnd = ::GetAncestor(parent_hwnd, GA_PARENT);
|
| + }
|
| + } while (parent_hwnd != NULL && parent_widget != NULL);
|
| +
|
| + return widget;
|
| +}
|
| +
|
| +// static
|
| +void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view,
|
| + Widget::Widgets* children) {
|
| + if (!native_view)
|
| + return;
|
| +
|
| + Widget* widget = Widget::GetWidgetForNativeView(native_view);
|
| + if (widget)
|
| + children->insert(widget);
|
| + EnumChildWindows(native_view, EnumerateChildWindowsForNativeWidgets,
|
| + reinterpret_cast<LPARAM>(children));
|
| +}
|
| +
|
| +// static
|
| +void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view,
|
| + gfx::NativeView new_parent) {
|
| + if (!native_view)
|
| + return;
|
| +
|
| + HWND previous_parent = ::GetParent(native_view);
|
| + if (previous_parent == new_parent)
|
| + return;
|
| +
|
| + Widget::Widgets widgets;
|
| + GetAllChildWidgets(native_view, &widgets);
|
| +
|
| + // First notify all the widgets that they are being disassociated
|
| + // from their previous parent.
|
| + for (Widget::Widgets::iterator it = widgets.begin();
|
| + it != widgets.end(); ++it) {
|
| + // TODO(beng): Rename this notification to NotifyNativeViewChanging()
|
| + // and eliminate the bool parameter.
|
| + (*it)->NotifyNativeViewHierarchyChanged(false, previous_parent);
|
| + }
|
| +
|
| + ::SetParent(native_view, new_parent);
|
| +
|
| + // And now, notify them that they have a brand new parent.
|
| + for (Widget::Widgets::iterator it = widgets.begin();
|
| + it != widgets.end(); ++it) {
|
| + (*it)->NotifyNativeViewHierarchyChanged(true, new_parent);
|
| + }
|
| +}
|
| +
|
| +// static
|
| +bool NativeWidgetPrivate::IsMouseButtonDown() {
|
| + return (GetKeyState(VK_LBUTTON) & 0x80) ||
|
| + (GetKeyState(VK_RBUTTON) & 0x80) ||
|
| + (GetKeyState(VK_MBUTTON) & 0x80) ||
|
| + (GetKeyState(VK_XBUTTON1) & 0x80) ||
|
| + (GetKeyState(VK_XBUTTON2) & 0x80);
|
| +}
|
| +
|
| +} // namespace internal
|
| +
|
| +} // namespace views
|
|
|