#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "ui/base/touch/touch_enabled.h"
+#include "ui/base/view_prop.h"
+#include "ui/base/win/internal_constants.h"
#include "ui/base/win/lock_state.h"
#include "ui/base/win/mouse_wheel_util.h"
#include "ui/base/win/shell.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/monitor_win.h"
#include "ui/views/widget/widget_hwnd_utils.h"
-#include "ui/views/win/appbar.h"
#include "ui/views/win/fullscreen_handler.h"
#include "ui/views/win/hwnd_message_handler_delegate.h"
#include "ui/views/win/scoped_fullscreen_visibility.h"
-#if !defined(USE_AURA)
-#include "ui/views/accessibility/native_view_accessibility_win.h"
-#include "ui/views/widget/child_window_message_processor.h"
-#endif
-
namespace views {
namespace {
std::vector<Widget*> owned_widgets;
};
-BOOL CALLBACK FindOwnedWindowsCallback(HWND hwnd, LPARAM param) {
- // TODO(beng): resolve wrt aura.
-#if !defined(USE_AURA)
- FindOwnedWindowsData* data = reinterpret_cast<FindOwnedWindowsData*>(param);
- if (GetWindow(hwnd, GW_OWNER) == data->window) {
- Widget* widget = Widget::GetWidgetForNativeView(hwnd);
- if (widget)
- data->owned_widgets.push_back(widget);
- }
-#endif
- return TRUE;
-}
-
// Enables or disables the menu item for the specified command and menu.
void EnableMenuItemByCommand(HMENU menu, UINT command, bool enabled) {
UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
return TRUE;
}
-#if !defined(USE_AURA)
-
-// 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;
-}
-
-#endif
-
// The thickness of an auto-hide taskbar in pixels.
const int kAutoHideTaskbarThicknessPx = 2;
}
}
+const int kTouchDownContextResetTimeout = 500;
+
+// Windows does not flag synthesized mouse messages from touch in all cases.
+// This causes us grief as we don't want to process touch and mouse messages
+// concurrently. Hack as per msdn is to check if the time difference between
+// the touch message and the mouse move is within 500 ms and at the same
+// location as the cursor.
+const int kSynthesizedMouseTouchMessagesTimeDifference = 500;
+
} // namespace
// A scoping class that prevents a window from being able to redraw in response
////////////////////////////////////////////////////////////////////////////////
// HWNDMessageHandler, public:
+long HWNDMessageHandler::last_touch_message_time_ = 0;
+
HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate)
: delegate_(delegate),
fullscreen_handler_(new FullscreenHandler),
waiting_for_close_now_(false),
remove_standard_frame_(false),
use_system_default_icon_(false),
- restore_focus_when_enabled_(false),
restored_enabled_(false),
current_cursor_(NULL),
previous_cursor_(NULL),
autohide_factory_(this),
id_generator_(0),
needs_scroll_styles_(false),
- in_size_loop_(false) {
+ in_size_loop_(false),
+ touch_down_context_(false),
+ last_mouse_hwheel_time_(0),
+ msg_handled_(FALSE) {
}
HWNDMessageHandler::~HWNDMessageHandler() {
}
}
#endif
+
+ prop_window_target_.reset(new ui::ViewProp(hwnd(),
+ ui::WindowEventTarget::kWin32InputEventTarget,
+ static_cast<ui::WindowEventTarget*>(this)));
}
void HWNDMessageHandler::InitModalType(ui::ModalType modal_type) {
SetWindowPos(hwnd(), NULL, 0, 0, 0, 0,
SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE |
SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER);
-
- if (!GetParent(hwnd()))
- NotifyOwnedWindowsParentClosing();
}
}
bool hide_on_escape) {
ReleaseCapture();
MoveLoopMouseWatcher watcher(this, hide_on_escape);
-#if defined(USE_AURA)
// In Aura, we handle touch events asynchronously. So we need to allow nested
// tasks while in windows move loop.
base::MessageLoop::ScopedNestableTaskAllower allow_nested(
base::MessageLoop::current());
-#endif
+
SendMessage(hwnd(), WM_SYSCOMMAND, SC_MOVE | 0x0002, GetMessagePos());
// Windows doesn't appear to offer a way to determine whether the user
// canceled the move or not. We assume if the user released the mouse it was
delegate_->HandleDestroyed();
}
- // Only top level widget should store/restore focus.
- if (message == WM_ACTIVATE && delegate_->CanSaveFocus())
+ if (message == WM_ACTIVATE && IsTopLevelWindow(window))
PostProcessActivateMessage(LOWORD(w_param), !!HIWORD(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(delegate_->CanSaveFocus());
- restore_focus_when_enabled_ = false;
- delegate_->RestoreFocusOnEnable();
- }
return result;
}
+LRESULT HWNDMessageHandler::HandleMouseMessage(unsigned int message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ // Don't track forwarded mouse messages. We expect the caller to track the
+ // mouse.
+ return HandleMouseEventInternal(message, w_param, l_param, false);
+}
+
+LRESULT HWNDMessageHandler::HandleTouchMessage(unsigned int message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ return OnTouchEvent(message, w_param, l_param);
+}
+
+LRESULT HWNDMessageHandler::HandleKeyboardMessage(unsigned int message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ return OnKeyEvent(message, w_param, l_param);
+}
+
+LRESULT HWNDMessageHandler::HandleScrollMessage(unsigned int message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ return OnScrollMessage(message, w_param, l_param);
+}
+
+LRESULT HWNDMessageHandler::HandleNcHitTestMessage(unsigned int message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ return OnNCHitTest(
+ gfx::Point(CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param)));
+}
+
////////////////////////////////////////////////////////////////////////////////
// HWNDMessageHandler, private:
int HWNDMessageHandler::GetAppbarAutohideEdges(HMONITOR monitor) {
autohide_factory_.InvalidateWeakPtrs();
- return Appbar::instance()->GetAutohideEdges(
- monitor,
- base::Bind(&HWNDMessageHandler::OnAppbarAutohideEdgesChanged,
- autohide_factory_.GetWeakPtr()));
+ return ViewsDelegate::views_delegate ?
+ ViewsDelegate::views_delegate->GetAppbarAutohideEdges(
+ monitor,
+ base::Bind(&HWNDMessageHandler::OnAppbarAutohideEdgesChanged,
+ autohide_factory_.GetWeakPtr())) :
+ ViewsDelegate::EDGE_BOTTOM;
}
void HWNDMessageHandler::OnAppbarAutohideEdgesChanged() {
void HWNDMessageHandler::PostProcessActivateMessage(int activation_state,
bool minimized) {
- DCHECK(delegate_->CanSaveFocus());
-
- bool active = activation_state != WA_INACTIVE && !minimized;
+ DCHECK(IsTopLevelWindow(hwnd()));
+ const bool active = activation_state != WA_INACTIVE && !minimized;
if (delegate_->CanActivate())
delegate_->HandleActivationChanged(active);
-
- if (!active) {
- // We might get activated/inactivated without being enabled, so we need to
- // clear restore_focus_when_enabled_.
- restore_focus_when_enabled_ = false;
- delegate_->SaveFocusOnDeactivate();
- } 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(hwnd())) {
- DCHECK(!restore_focus_when_enabled_);
- restore_focus_when_enabled_ = true;
- return;
- }
- delegate_->RestoreFocusOnActivate();
- }
}
void HWNDMessageHandler::RestoreEnabledIfNecessary() {
if (IsMaximized()) {
// Windows automatically adds a standard width border to all sides when a
// window is maximized.
- int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
+ int border_thickness =
+ GetSystemMetrics(SM_CXSIZEFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
if (remove_standard_frame_)
border_thickness -= 1;
*insets = gfx::Insets(
return result;
}
-void HWNDMessageHandler::NotifyOwnedWindowsParentClosing() {
- FindOwnedWindowsData data;
- data.window = hwnd();
- EnumThreadWindows(GetCurrentThreadId(), FindOwnedWindowsCallback,
- reinterpret_cast<LPARAM>(&data));
- for (size_t i = 0; i < data.owned_widgets.size(); ++i)
- data.owned_widgets[i]->OnOwnerClosing();
-}
-
void HWNDMessageHandler::LockUpdates(bool force) {
// We skip locked updates when Aero is on for two reasons:
// 1. Because it isn't necessary
return;
// We need to clip to the dirty rect ourselves.
- layered_window_contents_->sk_canvas()->save(SkCanvas::kClip_SaveFlag);
+ layered_window_contents_->sk_canvas()->save();
double scale = gfx::win::GetDeviceScaleFactor();
layered_window_contents_->sk_canvas()->scale(
SkScalar(scale),SkScalar(scale));
LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) {
use_layered_buffer_ = !!(window_ex_style() & WS_EX_LAYERED);
-#if defined(USE_AURA)
if (window_ex_style() & WS_EX_COMPOSITED) {
if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
// This is part of the magic to emulate layered windows with Aura
DwmExtendFrameIntoClientArea(hwnd(), &margins);
}
}
-#endif
fullscreen_handler_->set_hwnd(hwnd());
LRESULT HWNDMessageHandler::OnMouseActivate(UINT message,
WPARAM w_param,
LPARAM l_param) {
-#if defined(USE_AURA)
+ // Please refer to the comments in the header for the touch_down_context_
+ // member for the if statement below.
+ if (touch_down_context_)
+ return MA_NOACTIVATE;
+
// On Windows, if we select the menu item by touch and if the window at the
// location is another window on the same thread, that window gets a
// WM_MOUSEACTIVATE message and ends up activating itself, which is not
// current cursor location. We check for this property in our
// WM_MOUSEACTIVATE handler and don't activate the window if the property is
// set.
- if (::GetProp(hwnd(), kIgnoreTouchMouseActivateForWindow)) {
- ::RemoveProp(hwnd(), kIgnoreTouchMouseActivateForWindow);
+ if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) {
+ ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow);
return MA_NOACTIVATE;
}
// A child window activation should be treated as if we lost activation.
HWND child = ::RealChildWindowFromPoint(hwnd(), cursor_pos);
if (::IsWindow(child) && child != hwnd() && ::IsWindowVisible(child))
PostProcessActivateMessage(WA_INACTIVE, false);
-#endif
+
// TODO(beng): resolve this with the GetWindowLong() check on the subsequent
// line.
if (delegate_->IsWidgetWindow())
LRESULT HWNDMessageHandler::OnMouseRange(UINT message,
WPARAM w_param,
LPARAM l_param) {
-#if defined(USE_AURA)
- if (!touch_ids_.empty())
- return 0;
- // We handle touch events on Windows Aura. Windows generates synthesized
- // mouse messages in response to touch which we should ignore. However touch
- // messages are only received for the client area. We need to ignore the
- // synthesized mouse messages for all points in the client area and places
- // which return HTNOWHERE.
- if (ui::IsMouseEventFromTouch(message)) {
- LPARAM l_param_ht = l_param;
- // For mouse events (except wheel events), location is in window coordinates
- // and should be converted to screen coordinates for WM_NCHITTEST.
- if (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL) {
- POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param_ht);
- MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1);
- l_param_ht = MAKELPARAM(screen_point.x, screen_point.y);
- }
- LRESULT hittest = SendMessage(hwnd(), WM_NCHITTEST, 0, l_param_ht);
- if (hittest == HTCLIENT || hittest == HTNOWHERE)
- return 0;
- }
-#endif
- 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.
- POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param);
- MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1);
- w_param = SendMessage(hwnd(), WM_NCHITTEST, 0,
- MAKELPARAM(screen_point.x, screen_point.y));
- if (w_param == HTCAPTION || w_param == HTSYSMENU) {
- gfx::ShowSystemMenuAtPoint(hwnd(), gfx::Point(screen_point));
- return 0;
- }
- } else if (message == WM_NCLBUTTONDOWN && delegate_->IsUsingCustomFrame()) {
- 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 |= base::win::IsCtrlPressed() ? MK_CONTROL : 0;
- w_param |= base::win::IsShiftPressed() ? MK_SHIFT : 0;
- }
- }
- } else if (message == WM_NCRBUTTONDOWN &&
- (w_param == HTCAPTION || w_param == HTSYSMENU)) {
- is_right_mouse_pressed_on_caption_ = true;
- // We SetCapture() 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.
- SetCapture();
- }
- MSG msg = { hwnd(), message, w_param, l_param, GetMessageTime(),
- { CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param) } };
- ui::MouseEvent event(msg);
- if (!touch_ids_.empty() || ui::IsMouseEventFromTouch(message))
- event.set_flags(event.flags() | ui::EF_FROM_TOUCH);
-
- if (!(event.flags() & ui::EF_IS_NON_CLIENT))
- delegate_->HandleTooltipMouseMove(message, w_param, l_param);
-
- // Certain child windows forward mouse events to us. We don't want to track
- // these mouse moves as the child window will also send us a WM_MOUSELEAVE.
- if (event.type() == ui::ET_MOUSE_MOVED && !HasCapture() &&
- HIWORD(w_param) != SPECIAL_MOUSEMOVE_NOT_TO_BE_TRACKED) {
- // 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_->HandleMouseEvent(ui::MouseWheelEvent(msg))) ? 0 : 1;
- }
-
- // There are cases where the code handling the message destroys the window,
- // so use the weak ptr to check if destruction occured or not.
- base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
- bool handled = delegate_->HandleMouseEvent(event);
- if (!ref.get())
- return 0;
- if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU &&
- delegate_->IsUsingCustomFrame()) {
- // 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;
- }
-
- if (ref.get())
- SetMsgHandled(handled);
- return 0;
+ return HandleMouseEventInternal(message, w_param, l_param, true);
}
void HWNDMessageHandler::OnMove(const gfx::Point& point) {
}
}
const int autohide_edges = GetAppbarAutohideEdges(monitor);
- if (autohide_edges & Appbar::EDGE_LEFT)
+ if (autohide_edges & ViewsDelegate::EDGE_LEFT)
client_rect->left += kAutoHideTaskbarThicknessPx;
- if (autohide_edges & Appbar::EDGE_TOP) {
+ if (autohide_edges & ViewsDelegate::EDGE_TOP) {
if (!delegate_->IsUsingCustomFrame()) {
// Tricky bit. Due to a bug in DwmDefWindowProc()'s handling of
// WM_NCHITTEST, having any nonclient area atop the window causes the
client_rect->top += kAutoHideTaskbarThicknessPx;
}
}
- if (autohide_edges & Appbar::EDGE_RIGHT)
+ if (autohide_edges & ViewsDelegate::EDGE_RIGHT)
client_rect->right -= kAutoHideTaskbarThicknessPx;
- if (autohide_edges & Appbar::EDGE_BOTTOM)
+ if (autohide_edges & ViewsDelegate::EDGE_BOTTOM)
client_rect->bottom -= kAutoHideTaskbarThicknessPx;
// We cannot return WVR_REDRAW when there is nonclient area, or Windows
// Otherwise, we let Windows do all the native frame non-client handling for
// us.
-#if defined(USE_AURA)
LRESULT hit_test_code = DefWindowProc(hwnd(), WM_NCHITTEST, 0,
MAKELPARAM(point.x(), point.y()));
if (needs_scroll_styles_) {
// the vertical scrollar down arrow would be drawn.
// We check if the hittest coordinates lie in this region and if yes
// we return HTCLIENT.
- int border_width = ::GetSystemMetrics(SM_CXSIZEFRAME);
- int border_height = ::GetSystemMetrics(SM_CYSIZEFRAME);
+ int border_width = ::GetSystemMetrics(SM_CXSIZEFRAME) +
+ GetSystemMetrics(SM_CXPADDEDBORDER);
+ int border_height = ::GetSystemMetrics(SM_CYSIZEFRAME) +
+ GetSystemMetrics(SM_CXPADDEDBORDER);
int scroll_width = ::GetSystemMetrics(SM_CXVSCROLL);
int scroll_height = ::GetSystemMetrics(SM_CYVSCROLL);
RECT window_rect;
}
}
return hit_test_code;
-#else
- SetMsgHandled(FALSE);
-#endif
}
void HWNDMessageHandler::OnNCPaint(HRGN rgn) {
// Try to paint accelerated first.
if (!IsRectEmpty(&ps.rcPaint) &&
!delegate_->HandlePaintAccelerated(gfx::Rect(ps.rcPaint))) {
-#if defined(USE_AURA)
delegate_->HandlePaint(NULL);
-#else
- scoped_ptr<gfx::CanvasSkiaPaint> canvas(
- new gfx::CanvasSkiaPaint(hwnd(), display_dc, ps));
- delegate_->HandlePaint(canvas.get());
-#endif
}
EndPaint(hwnd(), &ps);
// invoked OnSize we ensure the RootView has been laid out.
ResetWindowRegion(false, true);
-#if defined(USE_AURA)
// We add the WS_VSCROLL and WS_HSCROLL styles to top level windows to ensure
// that legacy trackpad/trackpoint drivers generate the WM_VSCROLL and
// WM_HSCROLL messages and scrolling works.
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&AddScrollStylesToWindow, hwnd()));
}
-#endif
}
void HWNDMessageHandler::OnSysCommand(UINT notification_code,
// with the mouse/touch/keyboard, we flag as being in a size loop.
if ((notification_code & sc_mask) == SC_SIZE)
in_size_loop_ = true;
+ base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
DefWindowProc(hwnd(), WM_SYSCOMMAND, notification_code,
MAKELPARAM(point.x(), point.y()));
+ if (!ref.get())
+ return;
in_size_loop_ = false;
}
}
WPARAM w_param,
LPARAM l_param) {
// Handle touch events only on Aura for now.
-#if !defined(USE_AURA)
- SetMsgHandled(FALSE);
- return 0;
-#endif
int num_points = LOWORD(w_param);
scoped_ptr<TOUCHINPUT[]> input(new TOUCHINPUT[num_points]);
if (ui::GetTouchInputInfoWrapper(reinterpret_cast<HTOUCHINPUT>(l_param),
int flags = ui::GetModifiersFromKeyState();
TouchEvents touch_events;
for (int i = 0; i < num_points; ++i) {
+ POINT point;
+ point.x = TOUCH_COORD_TO_PIXEL(input[i].x) /
+ gfx::win::GetUndocumentedDPITouchScale();
+ point.y = TOUCH_COORD_TO_PIXEL(input[i].y) /
+ gfx::win::GetUndocumentedDPITouchScale();
+
+ if (base::win::GetVersion() == base::win::VERSION_WIN7) {
+ // Windows 7 sends touch events for touches in the non-client area,
+ // whereas Windows 8 does not. In order to unify the behaviour, always
+ // ignore touch events in the non-client area.
+ LPARAM l_param_ht = MAKELPARAM(point.x, point.y);
+ LRESULT hittest = SendMessage(hwnd(), WM_NCHITTEST, 0, l_param_ht);
+
+ if (hittest != HTCLIENT)
+ return 0;
+ }
+
+ ScreenToClient(hwnd(), &point);
+
+ last_touch_message_time_ = ::GetMessageTime();
+
ui::EventType touch_event_type = ui::ET_UNKNOWN;
if (input[i].dwFlags & TOUCHEVENTF_DOWN) {
touch_ids_.insert(input[i].dwID);
touch_event_type = ui::ET_TOUCH_PRESSED;
+ touch_down_context_ = true;
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&HWNDMessageHandler::ResetTouchDownContext,
+ weak_factory_.GetWeakPtr()),
+ base::TimeDelta::FromMilliseconds(kTouchDownContextResetTimeout));
} else if (input[i].dwFlags & TOUCHEVENTF_UP) {
touch_ids_.erase(input[i].dwID);
touch_event_type = ui::ET_TOUCH_RELEASED;
touch_event_type = ui::ET_TOUCH_MOVED;
}
if (touch_event_type != ui::ET_UNKNOWN) {
- POINT point;
- point.x = TOUCH_COORD_TO_PIXEL(input[i].x) /
- gfx::win::GetUndocumentedDPITouchScale();
- point.y = TOUCH_COORD_TO_PIXEL(input[i].y) /
- gfx::win::GetUndocumentedDPITouchScale();
-
- ScreenToClient(hwnd(), &point);
-
- ui::TouchEvent event(
- touch_event_type,
- gfx::Point(point.x, point.y),
- id_generator_.GetGeneratedID(input[i].dwID),
- base::TimeDelta::FromMilliseconds(input[i].dwTime));
+ base::TimeTicks now;
+ // input[i].dwTime doesn't necessarily relate to the system time at all,
+ // so use base::TimeTicks::HighResNow() if possible, or
+ // base::TimeTicks::Now() otherwise.
+ if (base::TimeTicks::IsHighResNowFastAndReliable())
+ now = base::TimeTicks::HighResNow();
+ else
+ now = base::TimeTicks::Now();
+ ui::TouchEvent event(touch_event_type,
+ gfx::Point(point.x, point.y),
+ id_generator_.GetGeneratedID(input[i].dwID),
+ now - base::TimeTicks());
event.set_flags(flags);
+ event.latency()->AddLatencyNumberWithTimestamp(
+ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT,
+ 0,
+ 0,
+ base::TimeTicks::FromInternalValue(
+ event.time_stamp().ToInternalValue()),
+ 1);
+
touch_events.push_back(event);
if (touch_event_type == ui::ET_TOUCH_RELEASED)
id_generator_.ReleaseNumber(input[i].dwID);
new_window_rect = monitor_rect;
} else if (IsMaximized()) {
new_window_rect = work_area;
- int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
+ int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME) +
+ GetSystemMetrics(SM_CXPADDEDBORDER);
new_window_rect.Inset(-border_thickness, -border_thickness);
} else {
new_window_rect = gfx::Rect(window_rect);
}
}
+ if (DidClientAreaSizeChange(window_pos))
+ delegate_->HandleWindowSizeChanging();
+
if (ScopedFullscreenVisibility::IsHiddenForFullscreen(hwnd())) {
// 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.
delegate_->HandleTouchEvent(touch_events[i]);
}
+void HWNDMessageHandler::ResetTouchDownContext() {
+ touch_down_context_ = false;
+}
+
+LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message,
+ WPARAM w_param,
+ LPARAM l_param,
+ bool track_mouse) {
+ if (!touch_ids_.empty())
+ return 0;
+ // We handle touch events on Windows Aura. Windows generates synthesized
+ // mouse messages in response to touch which we should ignore. However touch
+ // messages are only received for the client area. We need to ignore the
+ // synthesized mouse messages for all points in the client area and places
+ // which return HTNOWHERE.
+ if (ui::IsMouseEventFromTouch(message)) {
+ LPARAM l_param_ht = l_param;
+ // For mouse events (except wheel events), location is in window coordinates
+ // and should be converted to screen coordinates for WM_NCHITTEST.
+ if (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL) {
+ POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param_ht);
+ MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1);
+ l_param_ht = MAKELPARAM(screen_point.x, screen_point.y);
+ }
+ LRESULT hittest = SendMessage(hwnd(), WM_NCHITTEST, 0, l_param_ht);
+ if (hittest == HTCLIENT || hittest == HTNOWHERE)
+ return 0;
+ }
+
+ // Certain logitech drivers send the WM_MOUSEHWHEEL message to the parent
+ // followed by WM_MOUSEWHEEL messages to the child window causing a vertical
+ // scroll. We treat these WM_MOUSEWHEEL messages as WM_MOUSEHWHEEL
+ // messages.
+ if (message == WM_MOUSEHWHEEL)
+ last_mouse_hwheel_time_ = ::GetMessageTime();
+
+ if (message == WM_MOUSEWHEEL &&
+ ::GetMessageTime() == last_mouse_hwheel_time_) {
+ message = WM_MOUSEHWHEEL;
+ }
+
+ 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.
+ POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param);
+ MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1);
+ w_param = SendMessage(hwnd(), WM_NCHITTEST, 0,
+ MAKELPARAM(screen_point.x, screen_point.y));
+ if (w_param == HTCAPTION || w_param == HTSYSMENU) {
+ gfx::ShowSystemMenuAtPoint(hwnd(), gfx::Point(screen_point));
+ return 0;
+ }
+ } else if (message == WM_NCLBUTTONDOWN && delegate_->IsUsingCustomFrame()) {
+ 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 |= base::win::IsCtrlPressed() ? MK_CONTROL : 0;
+ w_param |= base::win::IsShiftPressed() ? MK_SHIFT : 0;
+ }
+ }
+ } else if (message == WM_NCRBUTTONDOWN &&
+ (w_param == HTCAPTION || w_param == HTSYSMENU)) {
+ is_right_mouse_pressed_on_caption_ = true;
+ // We SetCapture() 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.
+ SetCapture();
+ }
+ long message_time = GetMessageTime();
+ MSG msg = { hwnd(), message, w_param, l_param, message_time,
+ { CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param) } };
+ ui::MouseEvent event(msg);
+ if (IsSynthesizedMouseMessage(message, message_time, l_param))
+ event.set_flags(event.flags() | ui::EF_FROM_TOUCH);
+
+ if (!(event.flags() & ui::EF_IS_NON_CLIENT))
+ delegate_->HandleTooltipMouseMove(message, w_param, l_param);
+
+ if (event.type() == ui::ET_MOUSE_MOVED && !HasCapture() && track_mouse) {
+ // 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
+ // NativeWidget 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_->HandleMouseEvent(ui::MouseWheelEvent(msg))) ? 0 : 1;
+ }
+
+ // There are cases where the code handling the message destroys the window,
+ // so use the weak ptr to check if destruction occured or not.
+ base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr());
+ bool handled = delegate_->HandleMouseEvent(event);
+ if (!ref.get())
+ return 0;
+ if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU &&
+ delegate_->IsUsingCustomFrame()) {
+ // 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;
+ }
+
+ if (ref.get())
+ SetMsgHandled(handled);
+ return 0;
+}
+
+bool HWNDMessageHandler::IsSynthesizedMouseMessage(unsigned int message,
+ int message_time,
+ LPARAM l_param) {
+ if (ui::IsMouseEventFromTouch(message))
+ return true;
+ // Ignore mouse messages which occur at the same location as the current
+ // cursor position and within a time difference of 500 ms from the last
+ // touch message.
+ if (last_touch_message_time_ && message_time >= last_touch_message_time_ &&
+ ((message_time - last_touch_message_time_) <=
+ kSynthesizedMouseTouchMessagesTimeDifference)) {
+ POINT mouse_location = CR_POINT_INITIALIZER_FROM_LPARAM(l_param);
+ ::ClientToScreen(hwnd(), &mouse_location);
+ POINT cursor_pos = {0};
+ ::GetCursorPos(&cursor_pos);
+ if (memcmp(&cursor_pos, &mouse_location, sizeof(POINT)))
+ return false;
+ return true;
+ }
+ return false;
+}
+
} // namespace views