1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
|
// Copyright (c) 2010 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 "base/logging.h"
#include "base/utf_string_conversions.h"
#include "gfx/font.h"
#include "views/focus/focus_manager.h"
#include "views/screen.h"
#include "views/widget/root_view.h"
#include "views/widget/widget_gtk.h"
// WARNING: this implementation is good for a start, but it doesn't give us
// control of tooltip positioning both on mouse events and when showing from
// keyboard. We may need to write our own to give us the control we need.
namespace views {
static gfx::Font* LoadDefaultFont() {
// Create a tooltip widget and extract the font from it (we have to realize
// it to make sure the correct font gets set).
GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
gtk_widget_set_name(window, "gtk-tooltip");
GtkWidget* label = gtk_label_new("");
gtk_widget_show(label);
gtk_container_add(GTK_CONTAINER(window), label);
gtk_widget_realize(window);
GtkStyle* style = gtk_widget_get_style(label);
gfx::Font* font = new gfx::Font(gfx::Font::CreateFont(style->font_desc));
gtk_widget_destroy(window);
return font;
}
// static
int TooltipManager::GetTooltipHeight() {
// This is only used to position the tooltip, and we don't yet support
// positioning the tooltip, it isn't worth trying to implement this.
return 0;
}
// static
gfx::Font TooltipManager::GetDefaultFont() {
static gfx::Font* font = NULL;
if (!font)
font = LoadDefaultFont();
return *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;
}
// static
int TooltipManager::GetMaxWidth(int x, int y) {
gfx::Rect monitor_bounds =
Screen::GetMonitorAreaNearestPoint(gfx::Point(x, y));
// GtkLabel (gtk_label_ensure_layout) forces wrapping at this size. We mirror
// the size here otherwise tooltips wider than the size used by gtklabel end
// up with extraneous empty lines.
return monitor_bounds.width() == 0 ? 800 : (monitor_bounds.width() + 1) / 2;
}
TooltipManagerGtk::TooltipManagerGtk(WidgetGtk* widget)
: widget_(widget),
keyboard_view_(NULL),
tooltip_window_(widget->window_contents()) {
}
bool TooltipManagerGtk::ShowTooltip(int x, int y, bool for_keyboard,
GtkTooltip* tooltip) {
View* view = NULL;
gfx::Point view_loc;
if (keyboard_view_) {
view = keyboard_view_;
view_loc.SetPoint(view->width() / 2, view->height() / 2);
} else if (!for_keyboard) {
RootView* root_view = widget_->GetRootView();
view = root_view->GetViewForPoint(gfx::Point(x, y));
view_loc.SetPoint(x, y);
View::ConvertPointFromWidget(view, &view_loc);
} else {
FocusManager* focus_manager = widget_->GetFocusManager();
if (focus_manager) {
view = focus_manager->GetFocusedView();
if (view)
view_loc.SetPoint(view->width() / 2, view->height() / 2);
}
}
if (!view)
return false;
std::wstring text;
if (!view->GetTooltipText(view_loc, &text))
return false;
// Sets the area of the tooltip. This way if different views in the same
// widget have tooltips the tooltip doesn't get stuck at the same location.
gfx::Rect vis_bounds = view->GetVisibleBounds();
gfx::Point widget_loc(vis_bounds.origin());
View::ConvertPointToWidget(view, &widget_loc);
GdkRectangle tip_area = { widget_loc.x(), widget_loc.y(),
vis_bounds.width(), vis_bounds.height() };
gtk_tooltip_set_tip_area(tooltip, &tip_area);
int max_width, line_count;
gfx::Point screen_loc(x, y);
View::ConvertPointToScreen(widget_->GetRootView(), &screen_loc);
TrimTooltipToFit(&text, &max_width, &line_count, screen_loc.x(),
screen_loc.y());
tooltip_window_.SetTooltipText(text);
return true;
}
void TooltipManagerGtk::UpdateTooltip() {
// UpdateTooltip may be invoked after the widget has been destroyed.
GtkWidget* widget = widget_->GetNativeView();
if (!widget)
return;
GdkDisplay* display = gtk_widget_get_display(widget);
if (display)
gtk_tooltip_trigger_tooltip_query(display);
}
void TooltipManagerGtk::TooltipTextChanged(View* view) {
UpdateTooltip();
}
void TooltipManagerGtk::ShowKeyboardTooltip(View* view) {
if (view == keyboard_view_)
return; // We're already showing the tip for the specified view.
// We have to hide the current tooltip, then show again.
HideKeyboardTooltip();
std::wstring tooltip_text;
if (!view->GetTooltipText(gfx::Point(), &tooltip_text))
return; // The view doesn't have a tooltip, nothing to do.
keyboard_view_ = view;
if (!SendShowHelpSignal()) {
keyboard_view_ = NULL;
return;
}
}
void TooltipManagerGtk::HideKeyboardTooltip() {
if (!keyboard_view_)
return;
SendShowHelpSignal();
keyboard_view_ = NULL;
}
bool TooltipManagerGtk::SendShowHelpSignal() {
GtkWidget* widget = widget_->window_contents();
GType itype = G_TYPE_FROM_INSTANCE(G_OBJECT(widget));
guint signal_id;
GQuark detail;
if (!g_signal_parse_name("show_help", itype, &signal_id, &detail, FALSE)) {
NOTREACHED();
return false;
}
gboolean result;
g_signal_emit(widget, signal_id, 0, GTK_WIDGET_HELP_TOOLTIP, &result);
return true;
}
} // namespace views
|