Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/views/widget/tooltip_manager_win.h" | 5 #include "ui/views/widget/tooltip_manager_win.h" |
| 6 | 6 |
| 7 #include <windowsx.h> | 7 #include <windowsx.h> |
| 8 | 8 |
| 9 #include <limits> | 9 #include <limits> |
| 10 | 10 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 22 #include "ui/gfx/win/hwnd_util.h" | 22 #include "ui/gfx/win/hwnd_util.h" |
| 23 #include "ui/gfx/win/scoped_set_map_mode.h" | 23 #include "ui/gfx/win/scoped_set_map_mode.h" |
| 24 #include "ui/views/view.h" | 24 #include "ui/views/view.h" |
| 25 #include "ui/views/widget/monitor_win.h" | 25 #include "ui/views/widget/monitor_win.h" |
| 26 #include "ui/views/widget/widget.h" | 26 #include "ui/views/widget/widget.h" |
| 27 | 27 |
| 28 namespace views { | 28 namespace views { |
| 29 | 29 |
| 30 static int tooltip_height_ = 0; | 30 static int tooltip_height_ = 0; |
| 31 | 31 |
| 32 // Default timeout for the tooltip displayed using keyboard. | |
| 33 // Timeout is measured in milliseconds. | |
| 34 static const int kDefaultTimeout = 4000; | |
| 35 | |
| 36 // static | 32 // static |
| 37 int TooltipManager::GetTooltipHeight() { | 33 int TooltipManager::GetTooltipHeight() { |
| 38 DCHECK_GT(tooltip_height_, 0); | 34 DCHECK_GT(tooltip_height_, 0); |
| 39 return tooltip_height_; | 35 return tooltip_height_; |
| 40 } | 36 } |
| 41 | 37 |
| 42 static gfx::Font DetermineDefaultFont() { | 38 static gfx::Font DetermineDefaultFont() { |
| 43 HWND window = CreateWindowEx( | 39 HWND window = CreateWindowEx( |
| 44 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), | 40 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), |
| 45 TOOLTIPS_CLASS, NULL, 0 , 0, 0, 0, 0, NULL, NULL, NULL, NULL); | 41 TOOLTIPS_CLASS, NULL, 0 , 0, 0, 0, 0, NULL, NULL, NULL, NULL); |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 70 return monitor_bounds.width() == 0 ? 800 : monitor_bounds.width() - 30; | 66 return monitor_bounds.width() == 0 ? 800 : monitor_bounds.width() - 30; |
| 71 } | 67 } |
| 72 | 68 |
| 73 TooltipManagerWin::TooltipManagerWin(Widget* widget) | 69 TooltipManagerWin::TooltipManagerWin(Widget* widget) |
| 74 : widget_(widget), | 70 : widget_(widget), |
| 75 tooltip_hwnd_(NULL), | 71 tooltip_hwnd_(NULL), |
| 76 last_mouse_pos_(-1, -1), | 72 last_mouse_pos_(-1, -1), |
| 77 tooltip_showing_(false), | 73 tooltip_showing_(false), |
| 78 last_tooltip_view_(NULL), | 74 last_tooltip_view_(NULL), |
| 79 last_view_out_of_sync_(false), | 75 last_view_out_of_sync_(false), |
| 80 tooltip_width_(0), | 76 tooltip_width_(0) { |
| 81 keyboard_tooltip_hwnd_(NULL), | |
| 82 keyboard_tooltip_factory_(this) { | |
| 83 DCHECK(widget); | 77 DCHECK(widget); |
| 84 DCHECK(widget->GetNativeView()); | 78 DCHECK(widget->GetNativeView()); |
| 85 } | 79 } |
| 86 | 80 |
| 87 TooltipManagerWin::~TooltipManagerWin() { | 81 TooltipManagerWin::~TooltipManagerWin() { |
| 88 if (tooltip_hwnd_) | 82 if (tooltip_hwnd_) |
| 89 DestroyWindow(tooltip_hwnd_); | 83 DestroyWindow(tooltip_hwnd_); |
| 90 if (keyboard_tooltip_hwnd_) | |
| 91 DestroyWindow(keyboard_tooltip_hwnd_); | |
| 92 } | 84 } |
| 93 | 85 |
| 94 bool TooltipManagerWin::Init() { | 86 bool TooltipManagerWin::Init() { |
| 95 DCHECK(!tooltip_hwnd_); | 87 DCHECK(!tooltip_hwnd_); |
| 96 // Create the tooltip control. | 88 // Create the tooltip control. |
| 97 tooltip_hwnd_ = CreateWindowEx( | 89 tooltip_hwnd_ = CreateWindowEx( |
| 98 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), | 90 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), |
| 99 TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0, | 91 TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0, |
| 100 GetParent(), NULL, NULL, NULL); | 92 GetParent(), NULL, NULL, NULL); |
| 101 if (!tooltip_hwnd_) | 93 if (!tooltip_hwnd_) |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 140 | 132 |
| 141 void TooltipManagerWin::TooltipTextChanged(View* view) { | 133 void TooltipManagerWin::TooltipTextChanged(View* view) { |
| 142 if (view == last_tooltip_view_) | 134 if (view == last_tooltip_view_) |
| 143 UpdateTooltip(last_mouse_pos_); | 135 UpdateTooltip(last_mouse_pos_); |
| 144 } | 136 } |
| 145 | 137 |
| 146 LRESULT TooltipManagerWin::OnNotify(int w_param, | 138 LRESULT TooltipManagerWin::OnNotify(int w_param, |
| 147 NMHDR* l_param, | 139 NMHDR* l_param, |
| 148 bool* handled) { | 140 bool* handled) { |
| 149 *handled = false; | 141 *handled = false; |
| 150 if (l_param->hwndFrom == tooltip_hwnd_ && keyboard_tooltip_hwnd_ == NULL) { | 142 if (l_param->hwndFrom != tooltip_hwnd_) |
|
sky
2013/10/01 22:51:24
Making this an early return means I can indent all
| |
| 151 switch (l_param->code) { | 143 return 0; |
| 152 case TTN_GETDISPINFO: { | 144 |
| 153 if (last_view_out_of_sync_) { | 145 switch (l_param->code) { |
| 154 // View under the mouse is out of sync, determine it now. | 146 case TTN_GETDISPINFO: { |
| 155 View* root_view = widget_->GetRootView(); | 147 if (last_view_out_of_sync_) { |
| 156 last_tooltip_view_ = | 148 // View under the mouse is out of sync, determine it now. |
| 157 root_view->GetTooltipHandlerForPoint(last_mouse_pos_); | 149 View* root_view = widget_->GetRootView(); |
| 158 last_view_out_of_sync_ = false; | 150 last_tooltip_view_ = |
| 159 } | 151 root_view->GetTooltipHandlerForPoint(last_mouse_pos_); |
| 160 // Tooltip control is asking for the tooltip to display. | 152 last_view_out_of_sync_ = false; |
| 161 NMTTDISPINFOW* tooltip_info = | 153 } |
| 162 reinterpret_cast<NMTTDISPINFOW*>(l_param); | 154 // Tooltip control is asking for the tooltip to display. |
| 163 // Initialize the string, if we have a valid tooltip the string will | 155 NMTTDISPINFOW* tooltip_info = |
| 164 // get reset below. | 156 reinterpret_cast<NMTTDISPINFOW*>(l_param); |
| 165 tooltip_info->szText[0] = TEXT('\0'); | 157 // Initialize the string, if we have a valid tooltip the string will |
| 158 // get reset below. | |
| 159 tooltip_info->szText[0] = TEXT('\0'); | |
| 160 tooltip_text_.clear(); | |
| 161 tooltip_info->lpszText = NULL; | |
| 162 clipped_text_.clear(); | |
| 163 if (last_tooltip_view_ != NULL) { | |
| 166 tooltip_text_.clear(); | 164 tooltip_text_.clear(); |
| 167 tooltip_info->lpszText = NULL; | 165 // Mouse is over a View, ask the View for its tooltip. |
| 168 clipped_text_.clear(); | |
| 169 if (last_tooltip_view_ != NULL) { | |
| 170 tooltip_text_.clear(); | |
| 171 // Mouse is over a View, ask the View for its tooltip. | |
| 172 gfx::Point view_loc = last_mouse_pos_; | |
| 173 View::ConvertPointToTarget(widget_->GetRootView(), | |
| 174 last_tooltip_view_, &view_loc); | |
| 175 if (last_tooltip_view_->GetTooltipText(view_loc, &tooltip_text_) && | |
| 176 !tooltip_text_.empty()) { | |
| 177 // View has a valid tip, copy it into TOOLTIPINFO. | |
| 178 clipped_text_ = tooltip_text_; | |
| 179 gfx::Point screen_loc = last_mouse_pos_; | |
| 180 View::ConvertPointToScreen(widget_->GetRootView(), &screen_loc); | |
| 181 TrimTooltipToFit(&clipped_text_, &tooltip_width_, &line_count_, | |
| 182 screen_loc.x(), screen_loc.y(), | |
| 183 widget_->GetNativeView()); | |
| 184 // Adjust the clipped tooltip text for locale direction. | |
| 185 base::i18n::AdjustStringForLocaleDirection(&clipped_text_); | |
| 186 tooltip_info->lpszText = const_cast<WCHAR*>(clipped_text_.c_str()); | |
| 187 } else { | |
| 188 tooltip_text_.clear(); | |
| 189 } | |
| 190 } | |
| 191 *handled = true; | |
| 192 return 0; | |
| 193 } | |
| 194 case TTN_POP: | |
| 195 tooltip_showing_ = false; | |
| 196 *handled = true; | |
| 197 return 0; | |
| 198 case TTN_SHOW: { | |
| 199 *handled = true; | |
| 200 tooltip_showing_ = true; | |
| 201 // The tooltip is about to show, allow the view to position it | |
| 202 gfx::Point text_origin; | |
| 203 if (tooltip_height_ == 0) | |
| 204 tooltip_height_ = CalcTooltipHeight(); | |
| 205 gfx::Point view_loc = last_mouse_pos_; | 166 gfx::Point view_loc = last_mouse_pos_; |
| 206 View::ConvertPointToTarget(widget_->GetRootView(), | 167 View::ConvertPointToTarget(widget_->GetRootView(), |
| 207 last_tooltip_view_, &view_loc); | 168 last_tooltip_view_, &view_loc); |
| 208 if (last_tooltip_view_->GetTooltipTextOrigin(view_loc, &text_origin) && | 169 if (last_tooltip_view_->GetTooltipText(view_loc, &tooltip_text_) && |
| 209 SetTooltipPosition(text_origin.x(), text_origin.y())) { | 170 !tooltip_text_.empty()) { |
| 210 // Return true, otherwise the rectangle we specified is ignored. | 171 // View has a valid tip, copy it into TOOLTIPINFO. |
| 211 return TRUE; | 172 clipped_text_ = tooltip_text_; |
| 173 gfx::Point screen_loc = last_mouse_pos_; | |
| 174 View::ConvertPointToScreen(widget_->GetRootView(), &screen_loc); | |
| 175 TrimTooltipToFit(&clipped_text_, &tooltip_width_, &line_count_, | |
| 176 screen_loc.x(), screen_loc.y(), | |
| 177 widget_->GetNativeView()); | |
| 178 // Adjust the clipped tooltip text for locale direction. | |
| 179 base::i18n::AdjustStringForLocaleDirection(&clipped_text_); | |
| 180 tooltip_info->lpszText = const_cast<WCHAR*>(clipped_text_.c_str()); | |
| 181 } else { | |
| 182 tooltip_text_.clear(); | |
| 212 } | 183 } |
| 213 return 0; | |
| 214 } | 184 } |
| 215 default: | 185 *handled = true; |
| 216 // Fall through. | 186 return 0; |
| 217 break; | |
| 218 } | 187 } |
| 188 case TTN_POP: | |
| 189 tooltip_showing_ = false; | |
| 190 *handled = true; | |
| 191 return 0; | |
| 192 case TTN_SHOW: { | |
| 193 *handled = true; | |
| 194 tooltip_showing_ = true; | |
| 195 // The tooltip is about to show, allow the view to position it | |
| 196 gfx::Point text_origin; | |
| 197 if (tooltip_height_ == 0) | |
| 198 tooltip_height_ = CalcTooltipHeight(); | |
| 199 gfx::Point view_loc = last_mouse_pos_; | |
| 200 View::ConvertPointToTarget(widget_->GetRootView(), | |
| 201 last_tooltip_view_, &view_loc); | |
| 202 if (last_tooltip_view_->GetTooltipTextOrigin(view_loc, &text_origin) && | |
| 203 SetTooltipPosition(text_origin.x(), text_origin.y())) { | |
| 204 // Return true, otherwise the rectangle we specified is ignored. | |
| 205 return TRUE; | |
| 206 } | |
| 207 return 0; | |
| 208 } | |
| 209 default: | |
| 210 // Fall through. | |
| 211 break; | |
| 219 } | 212 } |
| 220 return 0; | 213 return 0; |
| 221 } | 214 } |
| 222 | 215 |
| 223 bool TooltipManagerWin::SetTooltipPosition(int text_x, int text_y) { | 216 bool TooltipManagerWin::SetTooltipPosition(int text_x, int text_y) { |
| 224 // NOTE: this really only tests that the y location fits on screen, but that | 217 // NOTE: this really only tests that the y location fits on screen, but that |
| 225 // is good enough for our usage. | 218 // is good enough for our usage. |
| 226 | 219 |
| 227 // Calculate the bounds the tooltip will get. | 220 // Calculate the bounds the tooltip will get. |
| 228 gfx::Point view_loc; | 221 gfx::Point view_loc; |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 306 if (u_msg >= WM_NCMOUSEMOVE && u_msg <= WM_NCXBUTTONDBLCLK) { | 299 if (u_msg >= WM_NCMOUSEMOVE && u_msg <= WM_NCXBUTTONDBLCLK) { |
| 307 // NC message coordinates are in screen coordinates. | 300 // NC message coordinates are in screen coordinates. |
| 308 POINT temp = mouse_pos_in_pixels.ToPOINT(); | 301 POINT temp = mouse_pos_in_pixels.ToPOINT(); |
| 309 ::MapWindowPoints(HWND_DESKTOP, GetParent(), &temp, 1); | 302 ::MapWindowPoints(HWND_DESKTOP, GetParent(), &temp, 1); |
| 310 mouse_pos_in_pixels.SetPoint(temp.x, temp.y); | 303 mouse_pos_in_pixels.SetPoint(temp.x, temp.y); |
| 311 mouse_pos = gfx::win::ScreenToDIPPoint(mouse_pos_in_pixels); | 304 mouse_pos = gfx::win::ScreenToDIPPoint(mouse_pos_in_pixels); |
| 312 } | 305 } |
| 313 | 306 |
| 314 if (u_msg != WM_MOUSEMOVE || last_mouse_pos_ != mouse_pos) { | 307 if (u_msg != WM_MOUSEMOVE || last_mouse_pos_ != mouse_pos) { |
| 315 last_mouse_pos_ = mouse_pos; | 308 last_mouse_pos_ = mouse_pos; |
| 316 HideKeyboardTooltip(); | |
| 317 UpdateTooltip(mouse_pos); | 309 UpdateTooltip(mouse_pos); |
| 318 } | 310 } |
| 319 // Forward the message onto the tooltip. | 311 // Forward the message onto the tooltip. |
| 320 MSG msg; | 312 MSG msg; |
| 321 msg.hwnd = GetParent(); | 313 msg.hwnd = GetParent(); |
| 322 msg.message = u_msg; | 314 msg.message = u_msg; |
| 323 msg.wParam = w_param; | 315 msg.wParam = w_param; |
| 324 msg.lParam = l_param; | 316 msg.lParam = l_param; |
| 325 SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, 0, (LPARAM)&msg); | 317 SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, 0, (LPARAM)&msg); |
| 326 } | 318 } |
| 327 | 319 |
| 328 void TooltipManagerWin::ShowKeyboardTooltip(View* focused_view) { | |
| 329 if (tooltip_showing_) { | |
| 330 SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); | |
| 331 tooltip_text_.clear(); | |
| 332 } | |
| 333 HideKeyboardTooltip(); | |
| 334 string16 tooltip_text; | |
| 335 if (!focused_view->GetTooltipText(gfx::Point(), &tooltip_text)) | |
| 336 return; | |
| 337 gfx::Rect focused_bounds = focused_view->bounds(); | |
| 338 gfx::Point screen_point; | |
| 339 focused_view->ConvertPointToScreen(focused_view, &screen_point); | |
| 340 keyboard_tooltip_hwnd_ = CreateWindowEx( | |
| 341 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), | |
| 342 TOOLTIPS_CLASS, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); | |
| 343 if (!keyboard_tooltip_hwnd_) | |
| 344 return; | |
| 345 | |
| 346 SendMessage(keyboard_tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, | |
| 347 std::numeric_limits<int16>::max()); | |
| 348 int tooltip_width; | |
| 349 int line_count; | |
| 350 TrimTooltipToFit(&tooltip_text, &tooltip_width, &line_count, | |
| 351 screen_point.x(), screen_point.y(), | |
| 352 widget_->GetNativeView()); | |
| 353 ReplaceSubstringsAfterOffset(&tooltip_text, 0, L"\n", L"\r\n"); | |
| 354 TOOLINFO keyboard_toolinfo; | |
| 355 memset(&keyboard_toolinfo, 0, sizeof(keyboard_toolinfo)); | |
| 356 keyboard_toolinfo.cbSize = sizeof(keyboard_toolinfo); | |
| 357 keyboard_toolinfo.hwnd = GetParent(); | |
| 358 keyboard_toolinfo.uFlags = TTF_TRACK | TTF_TRANSPARENT | TTF_IDISHWND; | |
| 359 keyboard_toolinfo.lpszText = const_cast<WCHAR*>(tooltip_text.c_str()); | |
| 360 SendMessage(keyboard_tooltip_hwnd_, TTM_ADDTOOL, 0, | |
| 361 reinterpret_cast<LPARAM>(&keyboard_toolinfo)); | |
| 362 SendMessage(keyboard_tooltip_hwnd_, TTM_TRACKACTIVATE, TRUE, | |
| 363 reinterpret_cast<LPARAM>(&keyboard_toolinfo)); | |
| 364 if (!tooltip_height_) | |
| 365 tooltip_height_ = CalcTooltipHeight(); | |
| 366 RECT rect_bounds = {screen_point.x(), | |
| 367 screen_point.y() + focused_bounds.height(), | |
| 368 screen_point.x() + tooltip_width, | |
| 369 screen_point.y() + focused_bounds.height() + | |
| 370 line_count * tooltip_height_ }; | |
| 371 gfx::Rect monitor_bounds = | |
| 372 views::GetMonitorBoundsForRect(gfx::Rect(rect_bounds)); | |
| 373 gfx::Rect fitted_bounds = gfx::Rect(rect_bounds); | |
| 374 fitted_bounds.AdjustToFit(monitor_bounds); | |
| 375 rect_bounds = fitted_bounds.ToRECT(); | |
| 376 ::SetWindowPos(keyboard_tooltip_hwnd_, NULL, rect_bounds.left, | |
| 377 rect_bounds.top, 0, 0, | |
| 378 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE); | |
| 379 base::MessageLoop::current()->PostDelayedTask( | |
| 380 FROM_HERE, | |
| 381 base::Bind(&TooltipManagerWin::DestroyKeyboardTooltipWindow, | |
| 382 keyboard_tooltip_factory_.GetWeakPtr(), | |
| 383 keyboard_tooltip_hwnd_), | |
| 384 base::TimeDelta::FromMilliseconds(kDefaultTimeout)); | |
| 385 } | |
| 386 | |
| 387 void TooltipManagerWin::HideKeyboardTooltip() { | |
| 388 if (keyboard_tooltip_hwnd_ != NULL) { | |
| 389 SendMessage(keyboard_tooltip_hwnd_, WM_CLOSE, 0, 0); | |
| 390 keyboard_tooltip_hwnd_ = NULL; | |
| 391 } | |
| 392 } | |
| 393 | |
| 394 void TooltipManagerWin::DestroyKeyboardTooltipWindow(HWND window_to_destroy) { | |
| 395 if (keyboard_tooltip_hwnd_ == window_to_destroy) | |
| 396 HideKeyboardTooltip(); | |
| 397 } | |
| 398 | |
| 399 } // namespace views | 320 } // namespace views |
| OLD | NEW |