From 2d82a8160b726349984aa7756304a30b8b0f348d Mon Sep 17 00:00:00 2001 From: "sky@chromium.org" Date: Thu, 28 May 2009 16:38:07 +0000 Subject: Splits TooltipManager so that it can be ported and stubs out the GTK side. BUG=none TEST=none Review URL: http://codereview.chromium.org/114054 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17076 0039d316-1c4b-4281-b951-d872f2087c98 --- views/widget/aero_tooltip_manager.cc | 14 +- views/widget/aero_tooltip_manager.h | 6 +- views/widget/tooltip_manager.cc | 448 ---------------------------------- views/widget/tooltip_manager.h | 143 +---------- views/widget/tooltip_manager_gtk.cc | 51 ++++ views/widget/tooltip_manager_gtk.h | 37 +++ views/widget/tooltip_manager_win.cc | 454 +++++++++++++++++++++++++++++++++++ views/widget/tooltip_manager_win.h | 160 ++++++++++++ views/widget/widget_gtk.cc | 6 +- views/widget/widget_gtk.h | 3 + views/widget/widget_win.cc | 4 +- views/widget/widget_win.h | 14 +- 12 files changed, 745 insertions(+), 595 deletions(-) delete mode 100644 views/widget/tooltip_manager.cc create mode 100644 views/widget/tooltip_manager_gtk.cc create mode 100644 views/widget/tooltip_manager_gtk.h create mode 100644 views/widget/tooltip_manager_win.cc create mode 100644 views/widget/tooltip_manager_win.h (limited to 'views/widget') diff --git a/views/widget/aero_tooltip_manager.cc b/views/widget/aero_tooltip_manager.cc index ca58c24..aef9e65 100644 --- a/views/widget/aero_tooltip_manager.cc +++ b/views/widget/aero_tooltip_manager.cc @@ -18,8 +18,8 @@ namespace views { /////////////////////////////////////////////////////////////////////////////// // AeroTooltipManager, public: -AeroTooltipManager::AeroTooltipManager(Widget* widget, HWND parent) - : TooltipManager(widget, parent), +AeroTooltipManager::AeroTooltipManager(Widget* widget) + : TooltipManagerWin(widget), initial_delay_(0) { } @@ -71,7 +71,7 @@ void AeroTooltipManager::Init() { tooltip_hwnd_ = CreateWindowEx( WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0, - parent_, NULL, NULL, NULL); + GetParent(), NULL, NULL, NULL); l10n_util::AdjustUIFontForWindow(tooltip_hwnd_); @@ -81,10 +81,10 @@ void AeroTooltipManager::Init() { // We use tracking tooltips on Vista to allow us to manually control the // visibility of the tooltip. toolinfo_.uFlags = TTF_TRANSPARENT | TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE; - toolinfo_.hwnd = parent_; - toolinfo_.uId = (UINT_PTR)parent_; + toolinfo_.hwnd = GetParent(); + toolinfo_.uId = (UINT_PTR)GetParent(); - // Setting this tells windows to call parent_ back (using a WM_NOTIFY + // Setting this tells windows to call GetParent() back (using a WM_NOTIFY // message) for the actual tooltip contents. toolinfo_.lpszText = LPSTR_TEXTCALLBACK; SetRectEmpty(&toolinfo_.rect); @@ -97,7 +97,7 @@ void AeroTooltipManager::OnTimer() { POINT pt; pt.x = last_mouse_x_; pt.y = last_mouse_y_; - ::ClientToScreen(parent_, &pt); + ::ClientToScreen(GetParent(), &pt); // Set the position and visibility. if (!tooltip_showing_) { diff --git a/views/widget/aero_tooltip_manager.h b/views/widget/aero_tooltip_manager.h index fe5856d..c81991c 100644 --- a/views/widget/aero_tooltip_manager.h +++ b/views/widget/aero_tooltip_manager.h @@ -7,7 +7,7 @@ #include "base/ref_counted.h" #include "base/task.h" -#include "views/widget/tooltip_manager.h" +#include "views/widget/tooltip_manager_win.h" namespace views { @@ -26,9 +26,9 @@ namespace views { // TTF_TRACKed tooltips. // // TODO(glen): Resolve this with Microsoft. -class AeroTooltipManager : public TooltipManager { +class AeroTooltipManager : public TooltipManagerWin { public: - AeroTooltipManager(Widget* widget, HWND parent); + explicit AeroTooltipManager(Widget* widget); virtual ~AeroTooltipManager(); virtual void OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param); diff --git a/views/widget/tooltip_manager.cc b/views/widget/tooltip_manager.cc deleted file mode 100644 index 9ef9ff9..0000000 --- a/views/widget/tooltip_manager.cc +++ /dev/null @@ -1,448 +0,0 @@ -// Copyright (c) 2006-2008 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/tooltip_manager.h" - -#include -#include - -#include "app/gfx/text_elider.h" -#include "app/l10n_util.h" -#include "app/l10n_util_win.h" -#include "app/win_util.h" -#include "base/logging.h" -#include "base/message_loop.h" -#include "views/view.h" -#include "views/widget/root_view.h" -#include "views/widget/widget.h" - -namespace views { - -//static -int TooltipManager::tooltip_height_ = 0; - -// Default timeout for the tooltip displayed using keyboard. -// Timeout is mentioned in milliseconds. -static const int kDefaultTimeout = 4000; - -// Maximum number of lines we allow in the tooltip. -static const int kMaxLines = 6; - -// Maximum number of characters we allow in a tooltip. -static const int kMaxTooltipLength = 1024; - -// Breaks |text| along line boundaries, placing each line of text into lines. -static void SplitTooltipString(const std::wstring& text, - std::vector* lines) { - size_t index = 0; - size_t next_index; - while ((next_index = text.find(TooltipManager::GetLineSeparator(), index)) - != std::wstring::npos && lines->size() < kMaxLines) { - lines->push_back(text.substr(index, next_index - index)); - index = next_index + TooltipManager::GetLineSeparator().size(); - } - if (next_index != text.size() && lines->size() < kMaxLines) - lines->push_back(text.substr(index, text.size() - index)); -} - -// static -int TooltipManager::GetTooltipHeight() { - DCHECK(tooltip_height_ > 0); - return tooltip_height_; -} - -static gfx::Font DetermineDefaultFont() { - HWND window = CreateWindowEx( - WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), - TOOLTIPS_CLASS, NULL, 0 , 0, 0, 0, 0, NULL, NULL, NULL, NULL); - HFONT hfont = reinterpret_cast(SendMessage(window, WM_GETFONT, 0, 0)); - gfx::Font font = hfont ? gfx::Font::CreateFont(hfont) : gfx::Font(); - DestroyWindow(window); - return font; -} - -// static -gfx::Font TooltipManager::GetDefaultFont() { - static gfx::Font* font = NULL; - if (!font) - font = new gfx::Font(DetermineDefaultFont()); - return *font; -} - -// static -const std::wstring& TooltipManager::GetLineSeparator() { - static const std::wstring* separator = NULL; - if (!separator) - separator = new std::wstring(L"\r\n"); - return *separator; -} - -TooltipManager::TooltipManager(Widget* widget, HWND parent) - : widget_(widget), - parent_(parent), - last_mouse_x_(-1), - last_mouse_y_(-1), - tooltip_showing_(false), - last_tooltip_view_(NULL), - last_view_out_of_sync_(false), - tooltip_width_(0), - keyboard_tooltip_hwnd_(NULL), -#pragma warning(suppress: 4355) - keyboard_tooltip_factory_(this) { - DCHECK(widget && parent); - Init(); -} - -TooltipManager::~TooltipManager() { - if (tooltip_hwnd_) - DestroyWindow(tooltip_hwnd_); - if (keyboard_tooltip_hwnd_) - DestroyWindow(keyboard_tooltip_hwnd_); -} - -void TooltipManager::Init() { - // Create the tooltip control. - tooltip_hwnd_ = CreateWindowEx( - WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), - TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0, - parent_, NULL, NULL, NULL); - - l10n_util::AdjustUIFontForWindow(tooltip_hwnd_); - - // This effectively turns off clipping of tooltips. We need this otherwise - // multi-line text (\r\n) won't work right. The size doesn't really matter - // (just as long as its bigger than the monitor's width) as we clip to the - // screen size before rendering. - SendMessage(tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, - std::numeric_limits::max()); - - // Add one tool that is used for all tooltips. - toolinfo_.cbSize = sizeof(toolinfo_); - toolinfo_.uFlags = TTF_TRANSPARENT | TTF_IDISHWND; - toolinfo_.hwnd = parent_; - toolinfo_.uId = reinterpret_cast(parent_); - // Setting this tells windows to call parent_ back (using a WM_NOTIFY - // message) for the actual tooltip contents. - toolinfo_.lpszText = LPSTR_TEXTCALLBACK; - SetRectEmpty(&toolinfo_.rect); - SendMessage(tooltip_hwnd_, TTM_ADDTOOL, 0, (LPARAM)&toolinfo_); -} - -void TooltipManager::UpdateTooltip() { - // Set last_view_out_of_sync_ to indicate the view is currently out of sync. - // This doesn't update the view under the mouse immediately as it may cause - // timing problems. - last_view_out_of_sync_ = true; - last_tooltip_view_ = NULL; - // Hide the tooltip. - SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); -} - -void TooltipManager::TooltipTextChanged(View* view) { - if (view == last_tooltip_view_) - UpdateTooltip(last_mouse_x_, last_mouse_y_); -} - -LRESULT TooltipManager::OnNotify(int w_param, NMHDR* l_param, bool* handled) { - *handled = false; - if (l_param->hwndFrom == tooltip_hwnd_ && keyboard_tooltip_hwnd_ == NULL) { - switch (l_param->code) { - case TTN_GETDISPINFO: { - if (last_view_out_of_sync_) { - // View under the mouse is out of sync, determine it now. - RootView* root_view = widget_->GetRootView(); - last_tooltip_view_ = root_view->GetViewForPoint( - gfx::Point(last_mouse_x_, last_mouse_y_)); - last_view_out_of_sync_ = false; - } - // Tooltip control is asking for the tooltip to display. - NMTTDISPINFOW* tooltip_info = - reinterpret_cast(l_param); - // Initialize the string, if we have a valid tooltip the string will - // get reset below. - tooltip_info->szText[0] = TEXT('\0'); - tooltip_text_.clear(); - tooltip_info->lpszText = NULL; - clipped_text_.clear(); - if (last_tooltip_view_ != NULL) { - tooltip_text_.clear(); - // Mouse is over a View, ask the View for it's tooltip. - gfx::Point view_loc(last_mouse_x_, last_mouse_y_); - View::ConvertPointToView(widget_->GetRootView(), - last_tooltip_view_, &view_loc); - if (last_tooltip_view_->GetTooltipText(view_loc.x(), view_loc.y(), - &tooltip_text_) && - !tooltip_text_.empty()) { - // View has a valid tip, copy it into TOOLTIPINFO. - clipped_text_ = tooltip_text_; - TrimTooltipToFit(&clipped_text_, &tooltip_width_, &line_count_, - last_mouse_x_, last_mouse_y_, tooltip_hwnd_); - // Adjust the clipped tooltip text for locale direction. - l10n_util::AdjustStringForLocaleDirection(clipped_text_, - &clipped_text_); - tooltip_info->lpszText = const_cast(clipped_text_.c_str()); - } else { - tooltip_text_.clear(); - } - } - *handled = true; - return 0; - } - case TTN_POP: - tooltip_showing_ = false; - *handled = true; - return 0; - case TTN_SHOW: { - *handled = true; - tooltip_showing_ = true; - // The tooltip is about to show, allow the view to position it - gfx::Point text_origin; - if (tooltip_height_ == 0) - tooltip_height_ = CalcTooltipHeight(); - gfx::Point view_loc(last_mouse_x_, last_mouse_y_); - View::ConvertPointToView(widget_->GetRootView(), - last_tooltip_view_, &view_loc); - if (last_tooltip_view_->GetTooltipTextOrigin( - view_loc.x(), view_loc.y(), &text_origin) && - SetTooltipPosition(text_origin.x(), text_origin.y())) { - // Return true, otherwise the rectangle we specified is ignored. - return TRUE; - } - return 0; - } - default: - // Fall through. - break; - } - } - return 0; -} - -bool TooltipManager::SetTooltipPosition(int text_x, int text_y) { - // NOTE: this really only tests that the y location fits on screen, but that - // is good enough for our usage. - - // Calculate the bounds the tooltip will get. - gfx::Point view_loc; - View::ConvertPointToScreen(last_tooltip_view_, &view_loc); - RECT bounds = { view_loc.x() + text_x, - view_loc.y() + text_y, - view_loc.x() + text_x + tooltip_width_, - view_loc.y() + line_count_ * GetTooltipHeight() }; - SendMessage(tooltip_hwnd_, TTM_ADJUSTRECT, TRUE, (LPARAM)&bounds); - - // Make sure the rectangle completely fits on the current monitor. If it - // doesn't, return false so that windows positions the tooltip at the - // default location. - gfx::Rect monitor_bounds = - win_util::GetMonitorBoundsForRect(gfx::Rect(bounds.left,bounds.right, - 0, 0)); - if (!monitor_bounds.Contains(gfx::Rect(bounds))) { - return false; - } - - ::SetWindowPos(tooltip_hwnd_, NULL, bounds.left, bounds.top, 0, 0, - SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE); - return true; -} - -int TooltipManager::CalcTooltipHeight() { - // Ask the tooltip for it's font. - int height; - HFONT hfont = reinterpret_cast( - SendMessage(tooltip_hwnd_, WM_GETFONT, 0, 0)); - if (hfont != NULL) { - HDC dc = GetDC(tooltip_hwnd_); - HFONT previous_font = static_cast(SelectObject(dc, hfont)); - int last_map_mode = SetMapMode(dc, MM_TEXT); - TEXTMETRIC font_metrics; - GetTextMetrics(dc, &font_metrics); - height = font_metrics.tmHeight; - // To avoid the DC referencing font_handle_, select the previous font. - SelectObject(dc, previous_font); - SetMapMode(dc, last_map_mode); - ReleaseDC(NULL, dc); - } else { - // Tooltip is using the system font. Use gfx::Font, which should pick - // up the system font. - height = gfx::Font().height(); - } - // Get the margins from the tooltip - RECT tooltip_margin; - SendMessage(tooltip_hwnd_, TTM_GETMARGIN, 0, (LPARAM)&tooltip_margin); - return height + tooltip_margin.top + tooltip_margin.bottom; -} - -void TooltipManager::TrimTooltipToFit(std::wstring* text, - int* max_width, - int* line_count, - int position_x, - int position_y, - HWND window) { - *max_width = 0; - *line_count = 0; - - // Clamp the tooltip length to kMaxTooltipLength so that we don't - // accidentally DOS the user with a mega tooltip (since Windows doesn't seem - // to do this itself). - if (text->length() > kMaxTooltipLength) - *text = text->substr(0, kMaxTooltipLength); - - // Determine the available width for the tooltip. - gfx::Point screen_loc(position_x, position_y); - View::ConvertPointToScreen(widget_->GetRootView(), &screen_loc); - gfx::Rect monitor_bounds = - win_util::GetMonitorBoundsForRect(gfx::Rect(screen_loc.x(), - screen_loc.y(), - 0, 0)); - RECT tooltip_margin; - SendMessage(window, TTM_GETMARGIN, 0, (LPARAM)&tooltip_margin); - const int available_width = monitor_bounds.width() - tooltip_margin.left - - tooltip_margin.right; - if (available_width <= 0) - return; - - // Split the string. - std::vector lines; - SplitTooltipString(*text, &lines); - *line_count = static_cast(lines.size()); - - // Format each line to fit. - gfx::Font font = GetDefaultFont(); - std::wstring result; - for (std::vector::iterator i = lines.begin(); i != lines.end(); - ++i) { - std::wstring elided_text = gfx::ElideText(*i, font, available_width); - *max_width = std::max(*max_width, font.GetStringWidth(elided_text)); - if (i == lines.begin() && i + 1 == lines.end()) { - *text = elided_text; - return; - } - if (!result.empty()) - result.append(GetLineSeparator()); - result.append(elided_text); - } - *text = result; -} - -void TooltipManager::UpdateTooltip(int x, int y) { - RootView* root_view = widget_->GetRootView(); - View* view = root_view->GetViewForPoint(gfx::Point(x, y)); - if (view != last_tooltip_view_) { - // NOTE: This *must* be sent regardless of the visibility of the tooltip. - // It triggers Windows to ask for the tooltip again. - SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); - last_tooltip_view_ = view; - } else if (last_tooltip_view_ != NULL) { - // Tooltip is showing, and mouse is over the same view. See if the tooltip - // text has changed. - gfx::Point view_point(x, y); - View::ConvertPointToView(root_view, last_tooltip_view_, &view_point); - std::wstring new_tooltip_text; - if (last_tooltip_view_->GetTooltipText(view_point.x(), view_point.y(), - &new_tooltip_text) && - new_tooltip_text != tooltip_text_) { - // The text has changed, hide the popup. - SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); - if (!new_tooltip_text.empty() && tooltip_showing_) { - // New text is valid, show the popup. - SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0); - } - } - } -} - -void TooltipManager::OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param) { - int x = GET_X_LPARAM(l_param); - int y = GET_Y_LPARAM(l_param); - - if (u_msg >= WM_NCMOUSEMOVE && u_msg <= WM_NCXBUTTONDBLCLK) { - // NC message coordinates are in screen coordinates. - gfx::Rect frame_bounds; - widget_->GetBounds(&frame_bounds, true); - x -= frame_bounds.x(); - y -= frame_bounds.y(); - } - - if (u_msg != WM_MOUSEMOVE || last_mouse_x_ != x || last_mouse_y_ != y) { - last_mouse_x_ = x; - last_mouse_y_ = y; - HideKeyboardTooltip(); - UpdateTooltip(x, y); - } - // Forward the message onto the tooltip. - MSG msg; - msg.hwnd = parent_; - msg.message = u_msg; - msg.wParam = w_param; - msg.lParam = l_param; - SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, 0, (LPARAM)&msg); -} - -void TooltipManager::ShowKeyboardTooltip(View* focused_view) { - if (tooltip_showing_) { - SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); - tooltip_text_.clear(); - } - HideKeyboardTooltip(); - std::wstring tooltip_text; - if (!focused_view->GetTooltipText(0, 0, &tooltip_text)) - return; - gfx::Rect focused_bounds = focused_view->bounds(); - gfx::Point screen_point; - focused_view->ConvertPointToScreen(focused_view, &screen_point); - gfx::Point relative_point_coordinates; - focused_view->ConvertPointToWidget(focused_view, &relative_point_coordinates); - keyboard_tooltip_hwnd_ = CreateWindowEx( - WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), - TOOLTIPS_CLASS, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); - SendMessage(keyboard_tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, - std::numeric_limits::max()); - int tooltip_width; - int line_count; - TrimTooltipToFit(&tooltip_text, &tooltip_width, &line_count, - relative_point_coordinates.x(), - relative_point_coordinates.y(), keyboard_tooltip_hwnd_); - TOOLINFO keyboard_toolinfo; - memset(&keyboard_toolinfo, 0, sizeof(keyboard_toolinfo)); - keyboard_toolinfo.cbSize = sizeof(keyboard_toolinfo); - keyboard_toolinfo.hwnd = parent_; - keyboard_toolinfo.uFlags = TTF_TRACK | TTF_TRANSPARENT | TTF_IDISHWND ; - keyboard_toolinfo.lpszText = const_cast(tooltip_text.c_str()); - SendMessage(keyboard_tooltip_hwnd_, TTM_ADDTOOL, 0, - reinterpret_cast(&keyboard_toolinfo)); - SendMessage(keyboard_tooltip_hwnd_, TTM_TRACKACTIVATE, TRUE, - reinterpret_cast(&keyboard_toolinfo)); - if (!tooltip_height_) - tooltip_height_ = CalcTooltipHeight(); - RECT rect_bounds = {screen_point.x(), - screen_point.y() + focused_bounds.height(), - screen_point.x() + tooltip_width, - screen_point.y() + focused_bounds.height() + - line_count * tooltip_height_ }; - gfx::Rect monitor_bounds = - win_util::GetMonitorBoundsForRect(gfx::Rect(rect_bounds)); - rect_bounds = gfx::Rect(rect_bounds).AdjustToFit(monitor_bounds).ToRECT(); - ::SetWindowPos(keyboard_tooltip_hwnd_, NULL, rect_bounds.left, - rect_bounds.top, 0, 0, - SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE); - MessageLoop::current()->PostDelayedTask(FROM_HERE, - keyboard_tooltip_factory_.NewRunnableMethod( - &TooltipManager::DestroyKeyboardTooltipWindow, keyboard_tooltip_hwnd_), - kDefaultTimeout); -} - -void TooltipManager::HideKeyboardTooltip() { - if (keyboard_tooltip_hwnd_ != NULL) { - SendMessage(keyboard_tooltip_hwnd_, WM_CLOSE, 0, 0); - keyboard_tooltip_hwnd_ = NULL; - } -} - -void TooltipManager::DestroyKeyboardTooltipWindow(HWND window_to_destroy) { - if (keyboard_tooltip_hwnd_ == window_to_destroy) - HideKeyboardTooltip(); -} - -} // namespace views diff --git a/views/widget/tooltip_manager.h b/views/widget/tooltip_manager.h index 5cfe2bf..cb48973 100644 --- a/views/widget/tooltip_manager.h +++ b/views/widget/tooltip_manager.h @@ -5,51 +5,21 @@ #ifndef VIEWS_WIDGET_TOOLTIP_MANAGER_H_ #define VIEWS_WIDGET_TOOLTIP_MANAGER_H_ -#include -#include - #include + #include "base/basictypes.h" -#include "base/task.h" namespace gfx { class Font; -} +} // namespace gfx namespace views { class View; -class Widget; - -// TooltipManager takes care of the wiring to support tooltips for Views. -// This class is intended to be used by Widgets. To use this, you must -// do the following: -// Add the following to your MSG_MAP: -// -// MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange) -// MESSAGE_RANGE_HANDLER(WM_NCMOUSEMOVE, WM_NCMOUSEMOVE, OnMouseRange) -// MSG_WM_NOTIFY(OnNotify) -// -// With the following implementations: -// LRESULT XXX::OnMouseRange(UINT u_msg, WPARAM w_param, LPARAM l_param, -// BOOL& handled) { -// tooltip_manager_->OnMouse(u_msg, w_param, l_param); -// handled = FALSE; -// return 0; -// } -// -// LRESULT XXX::OnNotify(int w_param, NMHDR* l_param) { -// bool handled; -// LRESULT result = tooltip_manager_->OnNotify(w_param, l_param, &handled); -// SetMsgHandled(handled); -// return result; -// } -// -// And of course you'll need to create the TooltipManager! -// -// Lastly, you'll need to override GetTooltipManager. -// -// See XPFrame for an example of this in action. + +// TooltipManager takes care of the wiring to support tooltips for Views. You +// almost never need to interact directly with TooltipManager, rather look to +// the various tooltip methods on View. class TooltipManager { public: // Returns the height of tooltips. This should only be invoked from within @@ -62,109 +32,22 @@ class TooltipManager { // Returns the separator for lines of text in a tooltip. static const std::wstring& GetLineSeparator(); - // Creates a TooltipManager for the specified Widget and parent window. - TooltipManager(Widget* widget, HWND parent); - virtual ~TooltipManager(); + TooltipManager() {} + virtual ~TooltipManager() {} // Notification that the view hierarchy has changed in some way. - void UpdateTooltip(); + virtual void UpdateTooltip() = 0; // Invoked when the tooltip text changes for the specified views. - void TooltipTextChanged(View* view); + virtual void TooltipTextChanged(View* view) = 0; // Invoked when toolbar icon gets focus. - void ShowKeyboardTooltip(View* view); + virtual void ShowKeyboardTooltip(View* view) = 0; // Invoked when toolbar loses focus. - void HideKeyboardTooltip(); - - // Message handlers. These forward to the tooltip control. - virtual void OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param); - LRESULT OnNotify(int w_param, NMHDR* l_param, bool* handled); - // Not used directly by TooltipManager, but provided for AeroTooltipManager. - virtual void OnMouseLeave() {} - - protected: - virtual void Init(); - - // Updates the tooltip for the specified location. - void UpdateTooltip(int x, int y); - - // Parent window the tooltip is added to. - HWND parent_; - - // Tooltip control window. - HWND tooltip_hwnd_; - - // Tooltip information. - TOOLINFO toolinfo_; - - // Last location of the mouse. This is in the coordinates of the rootview. - int last_mouse_x_; - int last_mouse_y_; - - // Whether or not the tooltip is showing. - bool tooltip_showing_; - - private: - // Sets the tooltip position based on the x/y position of the text. If the - // tooltip fits, true is returned. - bool SetTooltipPosition(int text_x, int text_y); - - // Calculates the preferred height for tooltips. This always returns a - // positive value. - int CalcTooltipHeight(); - - // Trims the tooltip to fit, setting text to the clipped result, width to the - // width (in pixels) of the clipped text and line_count to the number of lines - // of text in the tooltip. - void TrimTooltipToFit(std::wstring* text, - int* width, - int* line_count, - int position_x, - int position_y, - HWND window); - - // Invoked when the timer elapses and tooltip has to be destroyed. - void DestroyKeyboardTooltipWindow(HWND window_to_destroy); - - // Hosting Widget. - Widget* widget_; - - // The View the mouse is under. This is null if the mouse isn't under a - // View. - View* last_tooltip_view_; - - // Whether or not the view under the mouse needs to be refreshed. If this - // is true, when the tooltip is asked for the view under the mouse is - // refreshed. - bool last_view_out_of_sync_; - - // Text for tooltip from the view. - std::wstring tooltip_text_; - - // The clipped tooltip. - std::wstring clipped_text_; - - // Number of lines in the tooltip. - int line_count_; - - // Width of the last tooltip. - int tooltip_width_; - - // Height for a tooltip; lazily calculated. - static int tooltip_height_; - - // control window for tooltip displayed using keyboard. - HWND keyboard_tooltip_hwnd_; - - // Used to register DestroyTooltipWindow function with PostDelayedTask - // function. - ScopedRunnableMethodFactory keyboard_tooltip_factory_; - - DISALLOW_EVIL_CONSTRUCTORS(TooltipManager); + virtual void HideKeyboardTooltip() = 0; }; } // namespace views -#endif // VIEWS_WIDGET_TOOLTIP_MANAGER_H_ +#endif // VIEWS_WIDGET_TOOLTIP_MANAGER_H_ diff --git a/views/widget/tooltip_manager_gtk.cc b/views/widget/tooltip_manager_gtk.cc new file mode 100644 index 0000000..702514a --- /dev/null +++ b/views/widget/tooltip_manager_gtk.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2009 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/tooltip_manager_gtk.h" + +#include "app/gfx/font.h" +#include "base/logging.h" + +namespace views { + +// static +int TooltipManager::GetTooltipHeight() { + NOTIMPLEMENTED(); + return 0; +} + +// static +gfx::Font TooltipManager::GetDefaultFont() { + NOTIMPLEMENTED(); + return gfx::Font(); +} + +// static +const std::wstring& TooltipManager::GetLineSeparator() { + static std::wstring* line_separator = NULL; + if (!line_separator) + line_separator = new std::wstring(L"\n"); + return *line_separator; +} + +TooltipManagerGtk::TooltipManagerGtk(Widget* widget) : widget_(widget) { +} + +void TooltipManagerGtk::UpdateTooltip() { + NOTIMPLEMENTED(); +} + +void TooltipManagerGtk::TooltipTextChanged(View* view) { + NOTIMPLEMENTED(); +} + +void TooltipManagerGtk::ShowKeyboardTooltip(View* view) { + NOTIMPLEMENTED(); +} + +void TooltipManagerGtk::HideKeyboardTooltip() { + NOTIMPLEMENTED(); +} + +} // namespace views diff --git a/views/widget/tooltip_manager_gtk.h b/views/widget/tooltip_manager_gtk.h new file mode 100644 index 0000000..447f6ce --- /dev/null +++ b/views/widget/tooltip_manager_gtk.h @@ -0,0 +1,37 @@ +// Copyright (c) 2009 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. + +#ifndef VIEWS_WIDGET_TOOLTIP_MANAGER_GTK_H_ +#define VIEWS_WIDGET_TOOLTIP_MANAGER_GTK_H_ + +#include "views/widget/tooltip_manager.h" + +namespace views { + +class Widget; + +// TooltipManager takes care of the wiring to support tooltips for Views. You +// almost never need to interact directly with TooltipManager, rather look to +// the various tooltip methods on View. +class TooltipManagerGtk : public TooltipManager { + public: + explicit TooltipManagerGtk(Widget* widget); + virtual ~TooltipManagerGtk() {} + + // TooltipManager. + virtual void UpdateTooltip(); + virtual void TooltipTextChanged(View* view); + virtual void ShowKeyboardTooltip(View* view); + virtual void HideKeyboardTooltip(); + + private: + // Our owner. + Widget* widget_; + + DISALLOW_COPY_AND_ASSIGN(TooltipManagerGtk); +}; + +} // namespace views + +#endif // VIEWS_WIDGET_TOOLTIP_MANAGER_GTK_H_ diff --git a/views/widget/tooltip_manager_win.cc b/views/widget/tooltip_manager_win.cc new file mode 100644 index 0000000..70aff1e --- /dev/null +++ b/views/widget/tooltip_manager_win.cc @@ -0,0 +1,454 @@ +// Copyright (c) 2006-2008 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/tooltip_manager_win.h" + +#include +#include + +#include "app/gfx/text_elider.h" +#include "app/l10n_util.h" +#include "app/l10n_util_win.h" +#include "app/win_util.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "views/view.h" +#include "views/widget/root_view.h" +#include "views/widget/widget.h" + +namespace views { + +static int tooltip_height_ = 0; + +// Default timeout for the tooltip displayed using keyboard. +// Timeout is mentioned in milliseconds. +static const int kDefaultTimeout = 4000; + +// Maximum number of lines we allow in the tooltip. +static const int kMaxLines = 6; + +// Maximum number of characters we allow in a tooltip. +static const int kMaxTooltipLength = 1024; + +// Breaks |text| along line boundaries, placing each line of text into lines. +static void SplitTooltipString(const std::wstring& text, + std::vector* lines) { + size_t index = 0; + size_t next_index; + while ((next_index = text.find(TooltipManagerWin::GetLineSeparator(), index)) + != std::wstring::npos && lines->size() < kMaxLines) { + lines->push_back(text.substr(index, next_index - index)); + index = next_index + TooltipManagerWin::GetLineSeparator().size(); + } + if (next_index != text.size() && lines->size() < kMaxLines) + lines->push_back(text.substr(index, text.size() - index)); +} + +// static +int TooltipManager::GetTooltipHeight() { + DCHECK(tooltip_height_ > 0); + return tooltip_height_; +} + +static gfx::Font DetermineDefaultFont() { + HWND window = CreateWindowEx( + WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), + TOOLTIPS_CLASS, NULL, 0 , 0, 0, 0, 0, NULL, NULL, NULL, NULL); + HFONT hfont = reinterpret_cast(SendMessage(window, WM_GETFONT, 0, 0)); + gfx::Font font = hfont ? gfx::Font::CreateFont(hfont) : gfx::Font(); + DestroyWindow(window); + return font; +} + +// static +gfx::Font TooltipManager::GetDefaultFont() { + static gfx::Font* font = NULL; + if (!font) + font = new gfx::Font(DetermineDefaultFont()); + return *font; +} + +// static +const std::wstring& TooltipManager::GetLineSeparator() { + static const std::wstring* separator = NULL; + if (!separator) + separator = new std::wstring(L"\r\n"); + return *separator; +} + +TooltipManagerWin::TooltipManagerWin(Widget* widget) + : widget_(widget), + last_mouse_x_(-1), + last_mouse_y_(-1), + tooltip_showing_(false), + last_tooltip_view_(NULL), + last_view_out_of_sync_(false), + tooltip_width_(0), + keyboard_tooltip_hwnd_(NULL), +#pragma warning(suppress: 4355) + keyboard_tooltip_factory_(this) { + DCHECK(widget); + DCHECK(widget->GetNativeView()); + Init(); +} + +TooltipManagerWin::~TooltipManagerWin() { + if (tooltip_hwnd_) + DestroyWindow(tooltip_hwnd_); + if (keyboard_tooltip_hwnd_) + DestroyWindow(keyboard_tooltip_hwnd_); +} + +void TooltipManagerWin::Init() { + // Create the tooltip control. + tooltip_hwnd_ = CreateWindowEx( + WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), + TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0, + GetParent(), NULL, NULL, NULL); + + l10n_util::AdjustUIFontForWindow(tooltip_hwnd_); + + // This effectively turns off clipping of tooltips. We need this otherwise + // multi-line text (\r\n) won't work right. The size doesn't really matter + // (just as long as its bigger than the monitor's width) as we clip to the + // screen size before rendering. + SendMessage(tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, + std::numeric_limits::max()); + + // Add one tool that is used for all tooltips. + toolinfo_.cbSize = sizeof(toolinfo_); + toolinfo_.uFlags = TTF_TRANSPARENT | TTF_IDISHWND; + toolinfo_.hwnd = GetParent(); + toolinfo_.uId = reinterpret_cast(GetParent()); + // Setting this tells windows to call GetParent() back (using a WM_NOTIFY + // message) for the actual tooltip contents. + toolinfo_.lpszText = LPSTR_TEXTCALLBACK; + SetRectEmpty(&toolinfo_.rect); + SendMessage(tooltip_hwnd_, TTM_ADDTOOL, 0, (LPARAM)&toolinfo_); +} + +gfx::NativeView TooltipManagerWin::GetParent() { + return widget_->GetNativeView(); +} + +void TooltipManagerWin::UpdateTooltip() { + // Set last_view_out_of_sync_ to indicate the view is currently out of sync. + // This doesn't update the view under the mouse immediately as it may cause + // timing problems. + last_view_out_of_sync_ = true; + last_tooltip_view_ = NULL; + // Hide the tooltip. + SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); +} + +void TooltipManagerWin::TooltipTextChanged(View* view) { + if (view == last_tooltip_view_) + UpdateTooltip(last_mouse_x_, last_mouse_y_); +} + +LRESULT TooltipManagerWin::OnNotify(int w_param, + NMHDR* l_param, + bool* handled) { + *handled = false; + if (l_param->hwndFrom == tooltip_hwnd_ && keyboard_tooltip_hwnd_ == NULL) { + switch (l_param->code) { + case TTN_GETDISPINFO: { + if (last_view_out_of_sync_) { + // View under the mouse is out of sync, determine it now. + RootView* root_view = widget_->GetRootView(); + last_tooltip_view_ = root_view->GetViewForPoint( + gfx::Point(last_mouse_x_, last_mouse_y_)); + last_view_out_of_sync_ = false; + } + // Tooltip control is asking for the tooltip to display. + NMTTDISPINFOW* tooltip_info = + reinterpret_cast(l_param); + // Initialize the string, if we have a valid tooltip the string will + // get reset below. + tooltip_info->szText[0] = TEXT('\0'); + tooltip_text_.clear(); + tooltip_info->lpszText = NULL; + clipped_text_.clear(); + if (last_tooltip_view_ != NULL) { + tooltip_text_.clear(); + // Mouse is over a View, ask the View for it's tooltip. + gfx::Point view_loc(last_mouse_x_, last_mouse_y_); + View::ConvertPointToView(widget_->GetRootView(), + last_tooltip_view_, &view_loc); + if (last_tooltip_view_->GetTooltipText(view_loc.x(), view_loc.y(), + &tooltip_text_) && + !tooltip_text_.empty()) { + // View has a valid tip, copy it into TOOLTIPINFO. + clipped_text_ = tooltip_text_; + TrimTooltipToFit(&clipped_text_, &tooltip_width_, &line_count_, + last_mouse_x_, last_mouse_y_, tooltip_hwnd_); + // Adjust the clipped tooltip text for locale direction. + l10n_util::AdjustStringForLocaleDirection(clipped_text_, + &clipped_text_); + tooltip_info->lpszText = const_cast(clipped_text_.c_str()); + } else { + tooltip_text_.clear(); + } + } + *handled = true; + return 0; + } + case TTN_POP: + tooltip_showing_ = false; + *handled = true; + return 0; + case TTN_SHOW: { + *handled = true; + tooltip_showing_ = true; + // The tooltip is about to show, allow the view to position it + gfx::Point text_origin; + if (tooltip_height_ == 0) + tooltip_height_ = CalcTooltipHeight(); + gfx::Point view_loc(last_mouse_x_, last_mouse_y_); + View::ConvertPointToView(widget_->GetRootView(), + last_tooltip_view_, &view_loc); + if (last_tooltip_view_->GetTooltipTextOrigin( + view_loc.x(), view_loc.y(), &text_origin) && + SetTooltipPosition(text_origin.x(), text_origin.y())) { + // Return true, otherwise the rectangle we specified is ignored. + return TRUE; + } + return 0; + } + default: + // Fall through. + break; + } + } + return 0; +} + +bool TooltipManagerWin::SetTooltipPosition(int text_x, int text_y) { + // NOTE: this really only tests that the y location fits on screen, but that + // is good enough for our usage. + + // Calculate the bounds the tooltip will get. + gfx::Point view_loc; + View::ConvertPointToScreen(last_tooltip_view_, &view_loc); + RECT bounds = { view_loc.x() + text_x, + view_loc.y() + text_y, + view_loc.x() + text_x + tooltip_width_, + view_loc.y() + line_count_ * GetTooltipHeight() }; + SendMessage(tooltip_hwnd_, TTM_ADJUSTRECT, TRUE, (LPARAM)&bounds); + + // Make sure the rectangle completely fits on the current monitor. If it + // doesn't, return false so that windows positions the tooltip at the + // default location. + gfx::Rect monitor_bounds = + win_util::GetMonitorBoundsForRect(gfx::Rect(bounds.left,bounds.right, + 0, 0)); + if (!monitor_bounds.Contains(gfx::Rect(bounds))) { + return false; + } + + ::SetWindowPos(tooltip_hwnd_, NULL, bounds.left, bounds.top, 0, 0, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE); + return true; +} + +int TooltipManagerWin::CalcTooltipHeight() { + // Ask the tooltip for it's font. + int height; + HFONT hfont = reinterpret_cast( + SendMessage(tooltip_hwnd_, WM_GETFONT, 0, 0)); + if (hfont != NULL) { + HDC dc = GetDC(tooltip_hwnd_); + HFONT previous_font = static_cast(SelectObject(dc, hfont)); + int last_map_mode = SetMapMode(dc, MM_TEXT); + TEXTMETRIC font_metrics; + GetTextMetrics(dc, &font_metrics); + height = font_metrics.tmHeight; + // To avoid the DC referencing font_handle_, select the previous font. + SelectObject(dc, previous_font); + SetMapMode(dc, last_map_mode); + ReleaseDC(NULL, dc); + } else { + // Tooltip is using the system font. Use gfx::Font, which should pick + // up the system font. + height = gfx::Font().height(); + } + // Get the margins from the tooltip + RECT tooltip_margin; + SendMessage(tooltip_hwnd_, TTM_GETMARGIN, 0, (LPARAM)&tooltip_margin); + return height + tooltip_margin.top + tooltip_margin.bottom; +} + +void TooltipManagerWin::TrimTooltipToFit(std::wstring* text, + int* max_width, + int* line_count, + int position_x, + int position_y, + HWND window) { + *max_width = 0; + *line_count = 0; + + // Clamp the tooltip length to kMaxTooltipLength so that we don't + // accidentally DOS the user with a mega tooltip (since Windows doesn't seem + // to do this itself). + if (text->length() > kMaxTooltipLength) + *text = text->substr(0, kMaxTooltipLength); + + // Determine the available width for the tooltip. + gfx::Point screen_loc(position_x, position_y); + View::ConvertPointToScreen(widget_->GetRootView(), &screen_loc); + gfx::Rect monitor_bounds = + win_util::GetMonitorBoundsForRect(gfx::Rect(screen_loc.x(), + screen_loc.y(), + 0, 0)); + RECT tooltip_margin; + SendMessage(window, TTM_GETMARGIN, 0, (LPARAM)&tooltip_margin); + const int available_width = monitor_bounds.width() - tooltip_margin.left - + tooltip_margin.right; + if (available_width <= 0) + return; + + // Split the string. + std::vector lines; + SplitTooltipString(*text, &lines); + *line_count = static_cast(lines.size()); + + // Format each line to fit. + gfx::Font font = GetDefaultFont(); + std::wstring result; + for (std::vector::iterator i = lines.begin(); i != lines.end(); + ++i) { + std::wstring elided_text = gfx::ElideText(*i, font, available_width); + *max_width = std::max(*max_width, font.GetStringWidth(elided_text)); + if (i == lines.begin() && i + 1 == lines.end()) { + *text = elided_text; + return; + } + if (!result.empty()) + result.append(GetLineSeparator()); + result.append(elided_text); + } + *text = result; +} + +void TooltipManagerWin::UpdateTooltip(int x, int y) { + RootView* root_view = widget_->GetRootView(); + View* view = root_view->GetViewForPoint(gfx::Point(x, y)); + if (view != last_tooltip_view_) { + // NOTE: This *must* be sent regardless of the visibility of the tooltip. + // It triggers Windows to ask for the tooltip again. + SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); + last_tooltip_view_ = view; + } else if (last_tooltip_view_ != NULL) { + // Tooltip is showing, and mouse is over the same view. See if the tooltip + // text has changed. + gfx::Point view_point(x, y); + View::ConvertPointToView(root_view, last_tooltip_view_, &view_point); + std::wstring new_tooltip_text; + if (last_tooltip_view_->GetTooltipText(view_point.x(), view_point.y(), + &new_tooltip_text) && + new_tooltip_text != tooltip_text_) { + // The text has changed, hide the popup. + SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); + if (!new_tooltip_text.empty() && tooltip_showing_) { + // New text is valid, show the popup. + SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0); + } + } + } +} + +void TooltipManagerWin::OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param) { + int x = GET_X_LPARAM(l_param); + int y = GET_Y_LPARAM(l_param); + + if (u_msg >= WM_NCMOUSEMOVE && u_msg <= WM_NCXBUTTONDBLCLK) { + // NC message coordinates are in screen coordinates. + gfx::Rect frame_bounds; + widget_->GetBounds(&frame_bounds, true); + x -= frame_bounds.x(); + y -= frame_bounds.y(); + } + + if (u_msg != WM_MOUSEMOVE || last_mouse_x_ != x || last_mouse_y_ != y) { + last_mouse_x_ = x; + last_mouse_y_ = y; + HideKeyboardTooltip(); + UpdateTooltip(x, y); + } + // Forward the message onto the tooltip. + MSG msg; + msg.hwnd = GetParent(); + msg.message = u_msg; + msg.wParam = w_param; + msg.lParam = l_param; + SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, 0, (LPARAM)&msg); +} + +void TooltipManagerWin::ShowKeyboardTooltip(View* focused_view) { + if (tooltip_showing_) { + SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); + tooltip_text_.clear(); + } + HideKeyboardTooltip(); + std::wstring tooltip_text; + if (!focused_view->GetTooltipText(0, 0, &tooltip_text)) + return; + gfx::Rect focused_bounds = focused_view->bounds(); + gfx::Point screen_point; + focused_view->ConvertPointToScreen(focused_view, &screen_point); + gfx::Point relative_point_coordinates; + focused_view->ConvertPointToWidget(focused_view, &relative_point_coordinates); + keyboard_tooltip_hwnd_ = CreateWindowEx( + WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), + TOOLTIPS_CLASS, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + SendMessage(keyboard_tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, + std::numeric_limits::max()); + int tooltip_width; + int line_count; + TrimTooltipToFit(&tooltip_text, &tooltip_width, &line_count, + relative_point_coordinates.x(), + relative_point_coordinates.y(), keyboard_tooltip_hwnd_); + TOOLINFO keyboard_toolinfo; + memset(&keyboard_toolinfo, 0, sizeof(keyboard_toolinfo)); + keyboard_toolinfo.cbSize = sizeof(keyboard_toolinfo); + keyboard_toolinfo.hwnd = GetParent(); + keyboard_toolinfo.uFlags = TTF_TRACK | TTF_TRANSPARENT | TTF_IDISHWND ; + keyboard_toolinfo.lpszText = const_cast(tooltip_text.c_str()); + SendMessage(keyboard_tooltip_hwnd_, TTM_ADDTOOL, 0, + reinterpret_cast(&keyboard_toolinfo)); + SendMessage(keyboard_tooltip_hwnd_, TTM_TRACKACTIVATE, TRUE, + reinterpret_cast(&keyboard_toolinfo)); + if (!tooltip_height_) + tooltip_height_ = CalcTooltipHeight(); + RECT rect_bounds = {screen_point.x(), + screen_point.y() + focused_bounds.height(), + screen_point.x() + tooltip_width, + screen_point.y() + focused_bounds.height() + + line_count * tooltip_height_ }; + gfx::Rect monitor_bounds = + win_util::GetMonitorBoundsForRect(gfx::Rect(rect_bounds)); + rect_bounds = gfx::Rect(rect_bounds).AdjustToFit(monitor_bounds).ToRECT(); + ::SetWindowPos(keyboard_tooltip_hwnd_, NULL, rect_bounds.left, + rect_bounds.top, 0, 0, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE); + MessageLoop::current()->PostDelayedTask(FROM_HERE, + keyboard_tooltip_factory_.NewRunnableMethod( + &TooltipManagerWin::DestroyKeyboardTooltipWindow, + keyboard_tooltip_hwnd_), + kDefaultTimeout); +} + +void TooltipManagerWin::HideKeyboardTooltip() { + if (keyboard_tooltip_hwnd_ != NULL) { + SendMessage(keyboard_tooltip_hwnd_, WM_CLOSE, 0, 0); + keyboard_tooltip_hwnd_ = NULL; + } +} + +void TooltipManagerWin::DestroyKeyboardTooltipWindow(HWND window_to_destroy) { + if (keyboard_tooltip_hwnd_ == window_to_destroy) + HideKeyboardTooltip(); +} + +} // namespace views diff --git a/views/widget/tooltip_manager_win.h b/views/widget/tooltip_manager_win.h new file mode 100644 index 0000000..b2213c3 --- /dev/null +++ b/views/widget/tooltip_manager_win.h @@ -0,0 +1,160 @@ +// Copyright (c) 2006-2008 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. + +#ifndef VIEWS_WIDGET_TOOLTIP_MANAGER_WIN_H_ +#define VIEWS_WIDGET_TOOLTIP_MANAGER_WIN_H_ + +#include +#include +#include + +#include "base/basictypes.h" +#include "base/gfx/native_widget_types.h" +#include "base/task.h" +#include "views/widget/tooltip_manager.h" + +namespace gfx { +class Font; +} + +namespace views { + +class View; +class Widget; + +// TooltipManager implementation for Windows. +// +// This class is intended to be used by WidgetWin. To use this, you must +// do the following: +// Add the following to your MSG_MAP: +// +// MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange) +// MESSAGE_RANGE_HANDLER(WM_NCMOUSEMOVE, WM_NCMOUSEMOVE, OnMouseRange) +// MSG_WM_NOTIFY(OnNotify) +// +// With the following implementations: +// LRESULT XXX::OnMouseRange(UINT u_msg, WPARAM w_param, LPARAM l_param, +// BOOL& handled) { +// tooltip_manager_->OnMouse(u_msg, w_param, l_param); +// handled = FALSE; +// return 0; +// } +// +// LRESULT XXX::OnNotify(int w_param, NMHDR* l_param) { +// bool handled; +// LRESULT result = tooltip_manager_->OnNotify(w_param, l_param, &handled); +// SetMsgHandled(handled); +// return result; +// } +// +// And of course you'll need to create the TooltipManager! +// +// Lastly, you'll need to override GetTooltipManager. +// +// See WidgetWin for an example of this in action. +class TooltipManagerWin : public TooltipManager { + public: + // Creates a TooltipManager for the specified Widget and parent window. + explicit TooltipManagerWin(Widget* widget); + virtual ~TooltipManagerWin(); + + // Notification that the view hierarchy has changed in some way. + virtual void UpdateTooltip(); + + // Invoked when the tooltip text changes for the specified views. + virtual void TooltipTextChanged(View* view); + + // Invoked when toolbar icon gets focus. + virtual void ShowKeyboardTooltip(View* view); + + // Invoked when toolbar loses focus. + virtual void HideKeyboardTooltip(); + + // Message handlers. These forward to the tooltip control. + virtual void OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param); + LRESULT OnNotify(int w_param, NMHDR* l_param, bool* handled); + // Not used directly by TooltipManager, but provided for AeroTooltipManager. + virtual void OnMouseLeave() {} + + protected: + virtual void Init(); + + // Returns the Widget we're showing tooltips for. + gfx::NativeView GetParent(); + + // Updates the tooltip for the specified location. + void UpdateTooltip(int x, int y); + + // Tooltip control window. + HWND tooltip_hwnd_; + + // Tooltip information. + TOOLINFO toolinfo_; + + // Last location of the mouse. This is in the coordinates of the rootview. + int last_mouse_x_; + int last_mouse_y_; + + // Whether or not the tooltip is showing. + bool tooltip_showing_; + + private: + // Sets the tooltip position based on the x/y position of the text. If the + // tooltip fits, true is returned. + bool SetTooltipPosition(int text_x, int text_y); + + // Calculates the preferred height for tooltips. This always returns a + // positive value. + int CalcTooltipHeight(); + + // Trims the tooltip to fit, setting text to the clipped result, width to the + // width (in pixels) of the clipped text and line_count to the number of lines + // of text in the tooltip. + void TrimTooltipToFit(std::wstring* text, + int* width, + int* line_count, + int position_x, + int position_y, + HWND window); + + // Invoked when the timer elapses and tooltip has to be destroyed. + void DestroyKeyboardTooltipWindow(HWND window_to_destroy); + + // Hosting Widget. + Widget* widget_; + + // The View the mouse is under. This is null if the mouse isn't under a + // View. + View* last_tooltip_view_; + + // Whether or not the view under the mouse needs to be refreshed. If this + // is true, when the tooltip is asked for the view under the mouse is + // refreshed. + bool last_view_out_of_sync_; + + // Text for tooltip from the view. + std::wstring tooltip_text_; + + // The clipped tooltip. + std::wstring clipped_text_; + + // Number of lines in the tooltip. + int line_count_; + + // Width of the last tooltip. + int tooltip_width_; + + // control window for tooltip displayed using keyboard. + HWND keyboard_tooltip_hwnd_; + + // Used to register DestroyTooltipWindow function with PostDelayedTask + // function. + ScopedRunnableMethodFactory keyboard_tooltip_factory_; + + DISALLOW_COPY_AND_ASSIGN(TooltipManagerWin); +}; + +} // namespace views + +#endif // VIEWS_WIDGET_TOOLTIP_MANAGER_WIN_H_ diff --git a/views/widget/widget_gtk.cc b/views/widget/widget_gtk.cc index 96a2ad3..a1d30f6 100644 --- a/views/widget/widget_gtk.cc +++ b/views/widget/widget_gtk.cc @@ -7,6 +7,7 @@ #include "base/compiler_specific.h" #include "views/fill_layout.h" #include "views/widget/root_view.h" +#include "views/widget/tooltip_manager_gtk.h" #include "views/window/window_gtk.h" namespace views { @@ -152,6 +153,8 @@ void WidgetGtk::Init(GtkWidget* parent, // g_signal_connect(G_OBJECT(widget_), "drag_data_received", // G_CALLBACK(drag_data_received_event_cb), NULL); + tooltip_manager_.reset(new TooltipManagerGtk(this)); + if (type_ == TYPE_CHILD) { WidgetGtk* parent_widget = GetViewForNative(parent); parent_widget->AddChild(widget_); @@ -301,8 +304,7 @@ bool WidgetGtk::IsActive() const { } TooltipManager* WidgetGtk::GetTooltipManager() { - NOTIMPLEMENTED(); - return NULL; + return tooltip_manager_.get(); } bool WidgetGtk::GetAccelerator(int cmd_id, Accelerator* accelerator) { diff --git a/views/widget/widget_gtk.h b/views/widget/widget_gtk.h index 7cbc87e..3d57e4f 100644 --- a/views/widget/widget_gtk.h +++ b/views/widget/widget_gtk.h @@ -16,6 +16,7 @@ class Rect; namespace views { +class TooltipManagerGtk; class View; class WindowGtk; @@ -172,6 +173,8 @@ class WidgetGtk : public Widget, public MessageLoopForUI::Observer { // The root of the View hierarchy attached to this window. scoped_ptr root_view_; + scoped_ptr tooltip_manager_; + // If true, the mouse is currently down. bool is_mouse_down_; diff --git a/views/widget/widget_win.cc b/views/widget/widget_win.cc index 039ab36..516b771 100644 --- a/views/widget/widget_win.cc +++ b/views/widget/widget_win.cc @@ -203,9 +203,9 @@ void WidgetWin::Init(HWND parent, const gfx::Rect& bounds, // that window controls in Chrome windows don't flicker when you move your // mouse over them. See comment in aero_tooltip_manager.h. if (win_util::ShouldUseVistaFrame()) { - tooltip_manager_.reset(new AeroTooltipManager(this, GetNativeView())); + tooltip_manager_.reset(new AeroTooltipManager(this)); } else { - tooltip_manager_.reset(new TooltipManager(this, GetNativeView())); + tooltip_manager_.reset(new TooltipManagerWin(this)); } // This message initializes the window so that focus border are shown for diff --git a/views/widget/widget_win.h b/views/widget/widget_win.h index ebf6bfa..fa6bab6 100644 --- a/views/widget/widget_win.h +++ b/views/widget/widget_win.h @@ -24,7 +24,7 @@ class Rect; namespace views { class RootView; -class TooltipManager; +class TooltipManagerWin; class DefaultThemeProvider; class Window; @@ -506,7 +506,17 @@ class WidgetWin : public Widget, // Returns true if this WidgetWin is opaque. bool opaque() const { return opaque_; } + // The TooltipManager. + // WARNING: RootView's destructor calls into the TooltipManager. As such, this + // must be destroyed AFTER root_view_. This really only matters during + // WM_SESSIONEND, as normally the hwnd is destroyed which tiggers unsetting + // the widget in the RootView so that RootView's destructor doesn't call into + // the TooltipManager. + scoped_ptr tooltip_manager_; + // The root of the View hierarchy attached to this window. + // WARNING: see warning in tooltip_manager_ for ordering dependencies with + // this and tooltip_manager_. scoped_ptr root_view_; // Whether or not we have capture the mouse. @@ -515,8 +525,6 @@ class WidgetWin : public Widget, // If true, the mouse is currently down. bool is_mouse_down_; - scoped_ptr tooltip_manager_; - // Are a subclass of WindowWin? bool is_window_; -- cgit v1.1