Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(244)

Side by Side Diff: views/widget/tooltip_manager_win.cc

Issue 8598031: views: Move widget/ directory to ui/views. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: reland for real Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « views/widget/tooltip_manager_win.h ('k') | views/widget/widget.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "views/widget/tooltip_manager_win.h"
6
7 #include <windowsx.h>
8
9 #include <limits>
10
11 #include "base/bind.h"
12 #include "base/i18n/rtl.h"
13 #include "base/logging.h"
14 #include "base/message_loop.h"
15 #include "base/string_util.h"
16 #include "ui/base/l10n/l10n_util_win.h"
17 #include "ui/base/win/hwnd_util.h"
18 #include "ui/gfx/font.h"
19 #include "ui/gfx/screen.h"
20 #include "views/view.h"
21 #include "views/widget/monitor_win.h"
22 #include "views/widget/widget.h"
23
24 namespace views {
25
26 static int tooltip_height_ = 0;
27
28 // Default timeout for the tooltip displayed using keyboard.
29 // Timeout is mentioned in milliseconds.
30 static const int kDefaultTimeout = 4000;
31
32 // static
33 int TooltipManager::GetTooltipHeight() {
34 DCHECK_GT(tooltip_height_, 0);
35 return tooltip_height_;
36 }
37
38 static gfx::Font DetermineDefaultFont() {
39 HWND window = CreateWindowEx(
40 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
41 TOOLTIPS_CLASS, NULL, 0 , 0, 0, 0, 0, NULL, NULL, NULL, NULL);
42 if (!window)
43 return gfx::Font();
44 HFONT hfont = reinterpret_cast<HFONT>(SendMessage(window, WM_GETFONT, 0, 0));
45 gfx::Font font = hfont ? gfx::Font(hfont) : gfx::Font();
46 DestroyWindow(window);
47 return font;
48 }
49
50 // static
51 gfx::Font TooltipManager::GetDefaultFont() {
52 static gfx::Font* font = NULL;
53 if (!font)
54 font = new gfx::Font(DetermineDefaultFont());
55 return *font;
56 }
57
58 // static
59 int TooltipManager::GetMaxWidth(int x, int y) {
60 gfx::Rect monitor_bounds =
61 gfx::Screen::GetMonitorAreaNearestPoint(gfx::Point(x, y));
62 // Allow the tooltip to be almost as wide as the screen.
63 // Otherwise, we would truncate important text, since we're not word-wrapping
64 // the text onto multiple lines.
65 return monitor_bounds.width() == 0 ? 800 : monitor_bounds.width() - 30;
66 }
67
68 TooltipManagerWin::TooltipManagerWin(Widget* widget)
69 : widget_(widget),
70 tooltip_hwnd_(NULL),
71 last_mouse_pos_(-1, -1),
72 tooltip_showing_(false),
73 last_tooltip_view_(NULL),
74 last_view_out_of_sync_(false),
75 tooltip_width_(0),
76 keyboard_tooltip_hwnd_(NULL),
77 ALLOW_THIS_IN_INITIALIZER_LIST(keyboard_tooltip_factory_(this)) {
78 DCHECK(widget);
79 DCHECK(widget->GetNativeView());
80 }
81
82 TooltipManagerWin::~TooltipManagerWin() {
83 if (tooltip_hwnd_)
84 DestroyWindow(tooltip_hwnd_);
85 if (keyboard_tooltip_hwnd_)
86 DestroyWindow(keyboard_tooltip_hwnd_);
87 }
88
89 bool TooltipManagerWin::Init() {
90 DCHECK(!tooltip_hwnd_);
91 // Create the tooltip control.
92 tooltip_hwnd_ = CreateWindowEx(
93 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
94 TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0,
95 GetParent(), NULL, NULL, NULL);
96 if (!tooltip_hwnd_)
97 return false;
98
99 l10n_util::AdjustUIFontForWindow(tooltip_hwnd_);
100
101 // This effectively turns off clipping of tooltips. We need this otherwise
102 // multi-line text (\r\n) won't work right. The size doesn't really matter
103 // (just as long as its bigger than the monitor's width) as we clip to the
104 // screen size before rendering.
105 SendMessage(tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0,
106 std::numeric_limits<int16>::max());
107
108 // Add one tool that is used for all tooltips.
109 toolinfo_.cbSize = sizeof(toolinfo_);
110 toolinfo_.uFlags = TTF_TRANSPARENT | TTF_IDISHWND;
111 toolinfo_.hwnd = GetParent();
112 toolinfo_.uId = reinterpret_cast<UINT_PTR>(GetParent());
113 // Setting this tells windows to call GetParent() back (using a WM_NOTIFY
114 // message) for the actual tooltip contents.
115 toolinfo_.lpszText = LPSTR_TEXTCALLBACK;
116 toolinfo_.lpReserved = NULL;
117 SetRectEmpty(&toolinfo_.rect);
118 SendMessage(tooltip_hwnd_, TTM_ADDTOOL, 0, (LPARAM)&toolinfo_);
119 return true;
120 }
121
122 gfx::NativeView TooltipManagerWin::GetParent() {
123 return widget_->GetNativeView();
124 }
125
126 void TooltipManagerWin::UpdateTooltip() {
127 // Set last_view_out_of_sync_ to indicate the view is currently out of sync.
128 // This doesn't update the view under the mouse immediately as it may cause
129 // timing problems.
130 last_view_out_of_sync_ = true;
131 last_tooltip_view_ = NULL;
132 // Hide the tooltip.
133 SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
134 }
135
136 void TooltipManagerWin::TooltipTextChanged(View* view) {
137 if (view == last_tooltip_view_)
138 UpdateTooltip(last_mouse_pos_);
139 }
140
141 LRESULT TooltipManagerWin::OnNotify(int w_param,
142 NMHDR* l_param,
143 bool* handled) {
144 *handled = false;
145 if (l_param->hwndFrom == tooltip_hwnd_ && keyboard_tooltip_hwnd_ == NULL) {
146 switch (l_param->code) {
147 case TTN_GETDISPINFO: {
148 if (last_view_out_of_sync_) {
149 // View under the mouse is out of sync, determine it now.
150 View* root_view = widget_->GetRootView();
151 last_tooltip_view_ =
152 root_view->GetEventHandlerForPoint(last_mouse_pos_);
153 last_view_out_of_sync_ = false;
154 }
155 // Tooltip control is asking for the tooltip to display.
156 NMTTDISPINFOW* tooltip_info =
157 reinterpret_cast<NMTTDISPINFOW*>(l_param);
158 // Initialize the string, if we have a valid tooltip the string will
159 // get reset below.
160 tooltip_info->szText[0] = TEXT('\0');
161 tooltip_text_.clear();
162 tooltip_info->lpszText = NULL;
163 clipped_text_.clear();
164 if (last_tooltip_view_ != NULL) {
165 tooltip_text_.clear();
166 // Mouse is over a View, ask the View for its tooltip.
167 gfx::Point view_loc = last_mouse_pos_;
168 View::ConvertPointToView(widget_->GetRootView(),
169 last_tooltip_view_, &view_loc);
170 if (last_tooltip_view_->GetTooltipText(view_loc, &tooltip_text_) &&
171 !tooltip_text_.empty()) {
172 // View has a valid tip, copy it into TOOLTIPINFO.
173 clipped_text_ = tooltip_text_;
174 gfx::Point screen_loc = last_mouse_pos_;
175 View::ConvertPointToScreen(widget_->GetRootView(), &screen_loc);
176 TrimTooltipToFit(&clipped_text_, &tooltip_width_, &line_count_,
177 screen_loc.x(), screen_loc.y());
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();
183 }
184 }
185 *handled = true;
186 return 0;
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::ConvertPointToView(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;
212 }
213 }
214 return 0;
215 }
216
217 bool TooltipManagerWin::SetTooltipPosition(int text_x, int text_y) {
218 // NOTE: this really only tests that the y location fits on screen, but that
219 // is good enough for our usage.
220
221 // Calculate the bounds the tooltip will get.
222 gfx::Point view_loc;
223 View::ConvertPointToScreen(last_tooltip_view_, &view_loc);
224 RECT bounds = { view_loc.x() + text_x,
225 view_loc.y() + text_y,
226 view_loc.x() + text_x + tooltip_width_,
227 view_loc.y() + line_count_ * GetTooltipHeight() };
228 SendMessage(tooltip_hwnd_, TTM_ADJUSTRECT, TRUE, (LPARAM)&bounds);
229
230 // Make sure the rectangle completely fits on the current monitor. If it
231 // doesn't, return false so that windows positions the tooltip at the
232 // default location.
233 gfx::Rect monitor_bounds =
234 views::GetMonitorBoundsForRect(gfx::Rect(bounds.left, bounds.right,
235 0, 0));
236 if (!monitor_bounds.Contains(gfx::Rect(bounds))) {
237 return false;
238 }
239
240 ::SetWindowPos(tooltip_hwnd_, NULL, bounds.left, bounds.top, 0, 0,
241 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
242 return true;
243 }
244
245 int TooltipManagerWin::CalcTooltipHeight() {
246 // Ask the tooltip for it's font.
247 int height;
248 HFONT hfont = reinterpret_cast<HFONT>(
249 SendMessage(tooltip_hwnd_, WM_GETFONT, 0, 0));
250 if (hfont != NULL) {
251 HDC dc = GetDC(tooltip_hwnd_);
252 HFONT previous_font = static_cast<HFONT>(SelectObject(dc, hfont));
253 int last_map_mode = SetMapMode(dc, MM_TEXT);
254 TEXTMETRIC font_metrics;
255 GetTextMetrics(dc, &font_metrics);
256 height = font_metrics.tmHeight;
257 // To avoid the DC referencing font_handle_, select the previous font.
258 SelectObject(dc, previous_font);
259 SetMapMode(dc, last_map_mode);
260 ReleaseDC(NULL, dc);
261 } else {
262 // Tooltip is using the system font. Use gfx::Font, which should pick
263 // up the system font.
264 height = gfx::Font().GetHeight();
265 }
266 // Get the margins from the tooltip
267 RECT tooltip_margin;
268 SendMessage(tooltip_hwnd_, TTM_GETMARGIN, 0, (LPARAM)&tooltip_margin);
269 return height + tooltip_margin.top + tooltip_margin.bottom;
270 }
271
272 void TooltipManagerWin::UpdateTooltip(const gfx::Point& mouse_pos) {
273 View* root_view = widget_->GetRootView();
274 View* view = root_view->GetEventHandlerForPoint(mouse_pos);
275 if (view != last_tooltip_view_) {
276 // NOTE: This *must* be sent regardless of the visibility of the tooltip.
277 // It triggers Windows to ask for the tooltip again.
278 SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
279 last_tooltip_view_ = view;
280 } else if (last_tooltip_view_ != NULL) {
281 // Tooltip is showing, and mouse is over the same view. See if the tooltip
282 // text has changed.
283 gfx::Point view_point = mouse_pos;
284 View::ConvertPointToView(root_view, last_tooltip_view_, &view_point);
285 string16 new_tooltip_text;
286 bool has_tooltip_text =
287 last_tooltip_view_->GetTooltipText(view_point, &new_tooltip_text);
288 if (!has_tooltip_text || (new_tooltip_text != tooltip_text_)) {
289 // The text has changed, hide the popup.
290 SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
291 if (has_tooltip_text && !new_tooltip_text.empty() && tooltip_showing_) {
292 // New text is valid, show the popup.
293 SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0);
294 }
295 }
296 }
297 }
298
299 void TooltipManagerWin::OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param) {
300 gfx::Point mouse_pos(l_param);
301
302 if (u_msg >= WM_NCMOUSEMOVE && u_msg <= WM_NCXBUTTONDBLCLK) {
303 // NC message coordinates are in screen coordinates.
304 POINT temp = mouse_pos.ToPOINT();
305 ::MapWindowPoints(HWND_DESKTOP, GetParent(), &temp, 1);
306 mouse_pos.SetPoint(temp.x, temp.y);
307 }
308
309 if (u_msg != WM_MOUSEMOVE || last_mouse_pos_ != mouse_pos) {
310 last_mouse_pos_ = mouse_pos;
311 HideKeyboardTooltip();
312 UpdateTooltip(mouse_pos);
313 }
314 // Forward the message onto the tooltip.
315 MSG msg;
316 msg.hwnd = GetParent();
317 msg.message = u_msg;
318 msg.wParam = w_param;
319 msg.lParam = l_param;
320 SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, 0, (LPARAM)&msg);
321 }
322
323 void TooltipManagerWin::ShowKeyboardTooltip(View* focused_view) {
324 if (tooltip_showing_) {
325 SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
326 tooltip_text_.clear();
327 }
328 HideKeyboardTooltip();
329 string16 tooltip_text;
330 if (!focused_view->GetTooltipText(gfx::Point(), &tooltip_text))
331 return;
332 gfx::Rect focused_bounds = focused_view->bounds();
333 gfx::Point screen_point;
334 focused_view->ConvertPointToScreen(focused_view, &screen_point);
335 keyboard_tooltip_hwnd_ = CreateWindowEx(
336 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
337 TOOLTIPS_CLASS, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
338 if (!keyboard_tooltip_hwnd_)
339 return;
340
341 SendMessage(keyboard_tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0,
342 std::numeric_limits<int16>::max());
343 int tooltip_width;
344 int line_count;
345 TrimTooltipToFit(&tooltip_text, &tooltip_width, &line_count,
346 screen_point.x(), screen_point.y());
347 ReplaceSubstringsAfterOffset(&tooltip_text, 0, L"\n", L"\r\n");
348 TOOLINFO keyboard_toolinfo;
349 memset(&keyboard_toolinfo, 0, sizeof(keyboard_toolinfo));
350 keyboard_toolinfo.cbSize = sizeof(keyboard_toolinfo);
351 keyboard_toolinfo.hwnd = GetParent();
352 keyboard_toolinfo.uFlags = TTF_TRACK | TTF_TRANSPARENT | TTF_IDISHWND;
353 keyboard_toolinfo.lpszText = const_cast<WCHAR*>(tooltip_text.c_str());
354 SendMessage(keyboard_tooltip_hwnd_, TTM_ADDTOOL, 0,
355 reinterpret_cast<LPARAM>(&keyboard_toolinfo));
356 SendMessage(keyboard_tooltip_hwnd_, TTM_TRACKACTIVATE, TRUE,
357 reinterpret_cast<LPARAM>(&keyboard_toolinfo));
358 if (!tooltip_height_)
359 tooltip_height_ = CalcTooltipHeight();
360 RECT rect_bounds = {screen_point.x(),
361 screen_point.y() + focused_bounds.height(),
362 screen_point.x() + tooltip_width,
363 screen_point.y() + focused_bounds.height() +
364 line_count * tooltip_height_ };
365 gfx::Rect monitor_bounds =
366 views::GetMonitorBoundsForRect(gfx::Rect(rect_bounds));
367 rect_bounds = gfx::Rect(rect_bounds).AdjustToFit(monitor_bounds).ToRECT();
368 ::SetWindowPos(keyboard_tooltip_hwnd_, NULL, rect_bounds.left,
369 rect_bounds.top, 0, 0,
370 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
371 MessageLoop::current()->PostDelayedTask(
372 FROM_HERE,
373 base::Bind(&TooltipManagerWin::DestroyKeyboardTooltipWindow,
374 keyboard_tooltip_factory_.GetWeakPtr(),
375 keyboard_tooltip_hwnd_),
376 kDefaultTimeout);
377 }
378
379 void TooltipManagerWin::HideKeyboardTooltip() {
380 if (keyboard_tooltip_hwnd_ != NULL) {
381 SendMessage(keyboard_tooltip_hwnd_, WM_CLOSE, 0, 0);
382 keyboard_tooltip_hwnd_ = NULL;
383 }
384 }
385
386 void TooltipManagerWin::DestroyKeyboardTooltipWindow(HWND window_to_destroy) {
387 if (keyboard_tooltip_hwnd_ == window_to_destroy)
388 HideKeyboardTooltip();
389 }
390
391 } // namespace views
OLDNEW
« no previous file with comments | « views/widget/tooltip_manager_win.h ('k') | views/widget/widget.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698