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

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

Issue 114054: Splits TooltipManager so that it can be ported and stubs out the GTK... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 6 months 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.h ('k') | views/widget/tooltip_manager_gtk.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) 2006-2008 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.h"
6
7 #include <windowsx.h>
8 #include <limits>
9
10 #include "app/gfx/text_elider.h"
11 #include "app/l10n_util.h"
12 #include "app/l10n_util_win.h"
13 #include "app/win_util.h"
14 #include "base/logging.h"
15 #include "base/message_loop.h"
16 #include "views/view.h"
17 #include "views/widget/root_view.h"
18 #include "views/widget/widget.h"
19
20 namespace views {
21
22 //static
23 int TooltipManager::tooltip_height_ = 0;
24
25 // Default timeout for the tooltip displayed using keyboard.
26 // Timeout is mentioned in milliseconds.
27 static const int kDefaultTimeout = 4000;
28
29 // Maximum number of lines we allow in the tooltip.
30 static const int kMaxLines = 6;
31
32 // Maximum number of characters we allow in a tooltip.
33 static const int kMaxTooltipLength = 1024;
34
35 // Breaks |text| along line boundaries, placing each line of text into lines.
36 static void SplitTooltipString(const std::wstring& text,
37 std::vector<std::wstring>* lines) {
38 size_t index = 0;
39 size_t next_index;
40 while ((next_index = text.find(TooltipManager::GetLineSeparator(), index))
41 != std::wstring::npos && lines->size() < kMaxLines) {
42 lines->push_back(text.substr(index, next_index - index));
43 index = next_index + TooltipManager::GetLineSeparator().size();
44 }
45 if (next_index != text.size() && lines->size() < kMaxLines)
46 lines->push_back(text.substr(index, text.size() - index));
47 }
48
49 // static
50 int TooltipManager::GetTooltipHeight() {
51 DCHECK(tooltip_height_ > 0);
52 return tooltip_height_;
53 }
54
55 static gfx::Font DetermineDefaultFont() {
56 HWND window = CreateWindowEx(
57 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
58 TOOLTIPS_CLASS, NULL, 0 , 0, 0, 0, 0, NULL, NULL, NULL, NULL);
59 HFONT hfont = reinterpret_cast<HFONT>(SendMessage(window, WM_GETFONT, 0, 0));
60 gfx::Font font = hfont ? gfx::Font::CreateFont(hfont) : gfx::Font();
61 DestroyWindow(window);
62 return font;
63 }
64
65 // static
66 gfx::Font TooltipManager::GetDefaultFont() {
67 static gfx::Font* font = NULL;
68 if (!font)
69 font = new gfx::Font(DetermineDefaultFont());
70 return *font;
71 }
72
73 // static
74 const std::wstring& TooltipManager::GetLineSeparator() {
75 static const std::wstring* separator = NULL;
76 if (!separator)
77 separator = new std::wstring(L"\r\n");
78 return *separator;
79 }
80
81 TooltipManager::TooltipManager(Widget* widget, HWND parent)
82 : widget_(widget),
83 parent_(parent),
84 last_mouse_x_(-1),
85 last_mouse_y_(-1),
86 tooltip_showing_(false),
87 last_tooltip_view_(NULL),
88 last_view_out_of_sync_(false),
89 tooltip_width_(0),
90 keyboard_tooltip_hwnd_(NULL),
91 #pragma warning(suppress: 4355)
92 keyboard_tooltip_factory_(this) {
93 DCHECK(widget && parent);
94 Init();
95 }
96
97 TooltipManager::~TooltipManager() {
98 if (tooltip_hwnd_)
99 DestroyWindow(tooltip_hwnd_);
100 if (keyboard_tooltip_hwnd_)
101 DestroyWindow(keyboard_tooltip_hwnd_);
102 }
103
104 void TooltipManager::Init() {
105 // Create the tooltip control.
106 tooltip_hwnd_ = CreateWindowEx(
107 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
108 TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0,
109 parent_, NULL, NULL, NULL);
110
111 l10n_util::AdjustUIFontForWindow(tooltip_hwnd_);
112
113 // This effectively turns off clipping of tooltips. We need this otherwise
114 // multi-line text (\r\n) won't work right. The size doesn't really matter
115 // (just as long as its bigger than the monitor's width) as we clip to the
116 // screen size before rendering.
117 SendMessage(tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0,
118 std::numeric_limits<short>::max());
119
120 // Add one tool that is used for all tooltips.
121 toolinfo_.cbSize = sizeof(toolinfo_);
122 toolinfo_.uFlags = TTF_TRANSPARENT | TTF_IDISHWND;
123 toolinfo_.hwnd = parent_;
124 toolinfo_.uId = reinterpret_cast<UINT_PTR>(parent_);
125 // Setting this tells windows to call parent_ back (using a WM_NOTIFY
126 // message) for the actual tooltip contents.
127 toolinfo_.lpszText = LPSTR_TEXTCALLBACK;
128 SetRectEmpty(&toolinfo_.rect);
129 SendMessage(tooltip_hwnd_, TTM_ADDTOOL, 0, (LPARAM)&toolinfo_);
130 }
131
132 void TooltipManager::UpdateTooltip() {
133 // Set last_view_out_of_sync_ to indicate the view is currently out of sync.
134 // This doesn't update the view under the mouse immediately as it may cause
135 // timing problems.
136 last_view_out_of_sync_ = true;
137 last_tooltip_view_ = NULL;
138 // Hide the tooltip.
139 SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
140 }
141
142 void TooltipManager::TooltipTextChanged(View* view) {
143 if (view == last_tooltip_view_)
144 UpdateTooltip(last_mouse_x_, last_mouse_y_);
145 }
146
147 LRESULT TooltipManager::OnNotify(int w_param, NMHDR* l_param, bool* handled) {
148 *handled = false;
149 if (l_param->hwndFrom == tooltip_hwnd_ && keyboard_tooltip_hwnd_ == NULL) {
150 switch (l_param->code) {
151 case TTN_GETDISPINFO: {
152 if (last_view_out_of_sync_) {
153 // View under the mouse is out of sync, determine it now.
154 RootView* root_view = widget_->GetRootView();
155 last_tooltip_view_ = root_view->GetViewForPoint(
156 gfx::Point(last_mouse_x_, last_mouse_y_));
157 last_view_out_of_sync_ = false;
158 }
159 // Tooltip control is asking for the tooltip to display.
160 NMTTDISPINFOW* tooltip_info =
161 reinterpret_cast<NMTTDISPINFOW*>(l_param);
162 // Initialize the string, if we have a valid tooltip the string will
163 // get reset below.
164 tooltip_info->szText[0] = TEXT('\0');
165 tooltip_text_.clear();
166 tooltip_info->lpszText = NULL;
167 clipped_text_.clear();
168 if (last_tooltip_view_ != NULL) {
169 tooltip_text_.clear();
170 // Mouse is over a View, ask the View for it's tooltip.
171 gfx::Point view_loc(last_mouse_x_, last_mouse_y_);
172 View::ConvertPointToView(widget_->GetRootView(),
173 last_tooltip_view_, &view_loc);
174 if (last_tooltip_view_->GetTooltipText(view_loc.x(), view_loc.y(),
175 &tooltip_text_) &&
176 !tooltip_text_.empty()) {
177 // View has a valid tip, copy it into TOOLTIPINFO.
178 clipped_text_ = tooltip_text_;
179 TrimTooltipToFit(&clipped_text_, &tooltip_width_, &line_count_,
180 last_mouse_x_, last_mouse_y_, tooltip_hwnd_);
181 // Adjust the clipped tooltip text for locale direction.
182 l10n_util::AdjustStringForLocaleDirection(clipped_text_,
183 &clipped_text_);
184 tooltip_info->lpszText = const_cast<WCHAR*>(clipped_text_.c_str());
185 } else {
186 tooltip_text_.clear();
187 }
188 }
189 *handled = true;
190 return 0;
191 }
192 case TTN_POP:
193 tooltip_showing_ = false;
194 *handled = true;
195 return 0;
196 case TTN_SHOW: {
197 *handled = true;
198 tooltip_showing_ = true;
199 // The tooltip is about to show, allow the view to position it
200 gfx::Point text_origin;
201 if (tooltip_height_ == 0)
202 tooltip_height_ = CalcTooltipHeight();
203 gfx::Point view_loc(last_mouse_x_, last_mouse_y_);
204 View::ConvertPointToView(widget_->GetRootView(),
205 last_tooltip_view_, &view_loc);
206 if (last_tooltip_view_->GetTooltipTextOrigin(
207 view_loc.x(), view_loc.y(), &text_origin) &&
208 SetTooltipPosition(text_origin.x(), text_origin.y())) {
209 // Return true, otherwise the rectangle we specified is ignored.
210 return TRUE;
211 }
212 return 0;
213 }
214 default:
215 // Fall through.
216 break;
217 }
218 }
219 return 0;
220 }
221
222 bool TooltipManager::SetTooltipPosition(int text_x, int text_y) {
223 // NOTE: this really only tests that the y location fits on screen, but that
224 // is good enough for our usage.
225
226 // Calculate the bounds the tooltip will get.
227 gfx::Point view_loc;
228 View::ConvertPointToScreen(last_tooltip_view_, &view_loc);
229 RECT bounds = { view_loc.x() + text_x,
230 view_loc.y() + text_y,
231 view_loc.x() + text_x + tooltip_width_,
232 view_loc.y() + line_count_ * GetTooltipHeight() };
233 SendMessage(tooltip_hwnd_, TTM_ADJUSTRECT, TRUE, (LPARAM)&bounds);
234
235 // Make sure the rectangle completely fits on the current monitor. If it
236 // doesn't, return false so that windows positions the tooltip at the
237 // default location.
238 gfx::Rect monitor_bounds =
239 win_util::GetMonitorBoundsForRect(gfx::Rect(bounds.left,bounds.right,
240 0, 0));
241 if (!monitor_bounds.Contains(gfx::Rect(bounds))) {
242 return false;
243 }
244
245 ::SetWindowPos(tooltip_hwnd_, NULL, bounds.left, bounds.top, 0, 0,
246 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
247 return true;
248 }
249
250 int TooltipManager::CalcTooltipHeight() {
251 // Ask the tooltip for it's font.
252 int height;
253 HFONT hfont = reinterpret_cast<HFONT>(
254 SendMessage(tooltip_hwnd_, WM_GETFONT, 0, 0));
255 if (hfont != NULL) {
256 HDC dc = GetDC(tooltip_hwnd_);
257 HFONT previous_font = static_cast<HFONT>(SelectObject(dc, hfont));
258 int last_map_mode = SetMapMode(dc, MM_TEXT);
259 TEXTMETRIC font_metrics;
260 GetTextMetrics(dc, &font_metrics);
261 height = font_metrics.tmHeight;
262 // To avoid the DC referencing font_handle_, select the previous font.
263 SelectObject(dc, previous_font);
264 SetMapMode(dc, last_map_mode);
265 ReleaseDC(NULL, dc);
266 } else {
267 // Tooltip is using the system font. Use gfx::Font, which should pick
268 // up the system font.
269 height = gfx::Font().height();
270 }
271 // Get the margins from the tooltip
272 RECT tooltip_margin;
273 SendMessage(tooltip_hwnd_, TTM_GETMARGIN, 0, (LPARAM)&tooltip_margin);
274 return height + tooltip_margin.top + tooltip_margin.bottom;
275 }
276
277 void TooltipManager::TrimTooltipToFit(std::wstring* text,
278 int* max_width,
279 int* line_count,
280 int position_x,
281 int position_y,
282 HWND window) {
283 *max_width = 0;
284 *line_count = 0;
285
286 // Clamp the tooltip length to kMaxTooltipLength so that we don't
287 // accidentally DOS the user with a mega tooltip (since Windows doesn't seem
288 // to do this itself).
289 if (text->length() > kMaxTooltipLength)
290 *text = text->substr(0, kMaxTooltipLength);
291
292 // Determine the available width for the tooltip.
293 gfx::Point screen_loc(position_x, position_y);
294 View::ConvertPointToScreen(widget_->GetRootView(), &screen_loc);
295 gfx::Rect monitor_bounds =
296 win_util::GetMonitorBoundsForRect(gfx::Rect(screen_loc.x(),
297 screen_loc.y(),
298 0, 0));
299 RECT tooltip_margin;
300 SendMessage(window, TTM_GETMARGIN, 0, (LPARAM)&tooltip_margin);
301 const int available_width = monitor_bounds.width() - tooltip_margin.left -
302 tooltip_margin.right;
303 if (available_width <= 0)
304 return;
305
306 // Split the string.
307 std::vector<std::wstring> lines;
308 SplitTooltipString(*text, &lines);
309 *line_count = static_cast<int>(lines.size());
310
311 // Format each line to fit.
312 gfx::Font font = GetDefaultFont();
313 std::wstring result;
314 for (std::vector<std::wstring>::iterator i = lines.begin(); i != lines.end();
315 ++i) {
316 std::wstring elided_text = gfx::ElideText(*i, font, available_width);
317 *max_width = std::max(*max_width, font.GetStringWidth(elided_text));
318 if (i == lines.begin() && i + 1 == lines.end()) {
319 *text = elided_text;
320 return;
321 }
322 if (!result.empty())
323 result.append(GetLineSeparator());
324 result.append(elided_text);
325 }
326 *text = result;
327 }
328
329 void TooltipManager::UpdateTooltip(int x, int y) {
330 RootView* root_view = widget_->GetRootView();
331 View* view = root_view->GetViewForPoint(gfx::Point(x, y));
332 if (view != last_tooltip_view_) {
333 // NOTE: This *must* be sent regardless of the visibility of the tooltip.
334 // It triggers Windows to ask for the tooltip again.
335 SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
336 last_tooltip_view_ = view;
337 } else if (last_tooltip_view_ != NULL) {
338 // Tooltip is showing, and mouse is over the same view. See if the tooltip
339 // text has changed.
340 gfx::Point view_point(x, y);
341 View::ConvertPointToView(root_view, last_tooltip_view_, &view_point);
342 std::wstring new_tooltip_text;
343 if (last_tooltip_view_->GetTooltipText(view_point.x(), view_point.y(),
344 &new_tooltip_text) &&
345 new_tooltip_text != tooltip_text_) {
346 // The text has changed, hide the popup.
347 SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
348 if (!new_tooltip_text.empty() && tooltip_showing_) {
349 // New text is valid, show the popup.
350 SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0);
351 }
352 }
353 }
354 }
355
356 void TooltipManager::OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param) {
357 int x = GET_X_LPARAM(l_param);
358 int y = GET_Y_LPARAM(l_param);
359
360 if (u_msg >= WM_NCMOUSEMOVE && u_msg <= WM_NCXBUTTONDBLCLK) {
361 // NC message coordinates are in screen coordinates.
362 gfx::Rect frame_bounds;
363 widget_->GetBounds(&frame_bounds, true);
364 x -= frame_bounds.x();
365 y -= frame_bounds.y();
366 }
367
368 if (u_msg != WM_MOUSEMOVE || last_mouse_x_ != x || last_mouse_y_ != y) {
369 last_mouse_x_ = x;
370 last_mouse_y_ = y;
371 HideKeyboardTooltip();
372 UpdateTooltip(x, y);
373 }
374 // Forward the message onto the tooltip.
375 MSG msg;
376 msg.hwnd = parent_;
377 msg.message = u_msg;
378 msg.wParam = w_param;
379 msg.lParam = l_param;
380 SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, 0, (LPARAM)&msg);
381 }
382
383 void TooltipManager::ShowKeyboardTooltip(View* focused_view) {
384 if (tooltip_showing_) {
385 SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
386 tooltip_text_.clear();
387 }
388 HideKeyboardTooltip();
389 std::wstring tooltip_text;
390 if (!focused_view->GetTooltipText(0, 0, &tooltip_text))
391 return;
392 gfx::Rect focused_bounds = focused_view->bounds();
393 gfx::Point screen_point;
394 focused_view->ConvertPointToScreen(focused_view, &screen_point);
395 gfx::Point relative_point_coordinates;
396 focused_view->ConvertPointToWidget(focused_view, &relative_point_coordinates);
397 keyboard_tooltip_hwnd_ = CreateWindowEx(
398 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
399 TOOLTIPS_CLASS, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
400 SendMessage(keyboard_tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0,
401 std::numeric_limits<short>::max());
402 int tooltip_width;
403 int line_count;
404 TrimTooltipToFit(&tooltip_text, &tooltip_width, &line_count,
405 relative_point_coordinates.x(),
406 relative_point_coordinates.y(), keyboard_tooltip_hwnd_);
407 TOOLINFO keyboard_toolinfo;
408 memset(&keyboard_toolinfo, 0, sizeof(keyboard_toolinfo));
409 keyboard_toolinfo.cbSize = sizeof(keyboard_toolinfo);
410 keyboard_toolinfo.hwnd = parent_;
411 keyboard_toolinfo.uFlags = TTF_TRACK | TTF_TRANSPARENT | TTF_IDISHWND ;
412 keyboard_toolinfo.lpszText = const_cast<WCHAR*>(tooltip_text.c_str());
413 SendMessage(keyboard_tooltip_hwnd_, TTM_ADDTOOL, 0,
414 reinterpret_cast<LPARAM>(&keyboard_toolinfo));
415 SendMessage(keyboard_tooltip_hwnd_, TTM_TRACKACTIVATE, TRUE,
416 reinterpret_cast<LPARAM>(&keyboard_toolinfo));
417 if (!tooltip_height_)
418 tooltip_height_ = CalcTooltipHeight();
419 RECT rect_bounds = {screen_point.x(),
420 screen_point.y() + focused_bounds.height(),
421 screen_point.x() + tooltip_width,
422 screen_point.y() + focused_bounds.height() +
423 line_count * tooltip_height_ };
424 gfx::Rect monitor_bounds =
425 win_util::GetMonitorBoundsForRect(gfx::Rect(rect_bounds));
426 rect_bounds = gfx::Rect(rect_bounds).AdjustToFit(monitor_bounds).ToRECT();
427 ::SetWindowPos(keyboard_tooltip_hwnd_, NULL, rect_bounds.left,
428 rect_bounds.top, 0, 0,
429 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
430 MessageLoop::current()->PostDelayedTask(FROM_HERE,
431 keyboard_tooltip_factory_.NewRunnableMethod(
432 &TooltipManager::DestroyKeyboardTooltipWindow, keyboard_tooltip_hwnd_),
433 kDefaultTimeout);
434 }
435
436 void TooltipManager::HideKeyboardTooltip() {
437 if (keyboard_tooltip_hwnd_ != NULL) {
438 SendMessage(keyboard_tooltip_hwnd_, WM_CLOSE, 0, 0);
439 keyboard_tooltip_hwnd_ = NULL;
440 }
441 }
442
443 void TooltipManager::DestroyKeyboardTooltipWindow(HWND window_to_destroy) {
444 if (keyboard_tooltip_hwnd_ == window_to_destroy)
445 HideKeyboardTooltip();
446 }
447
448 } // namespace views
OLDNEW
« no previous file with comments | « views/widget/tooltip_manager.h ('k') | views/widget/tooltip_manager_gtk.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698