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

Side by Side Diff: ui/app_list/app_list_bubble_border.cc

Issue 10834140: aura: Fix launcher tooltips: (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: patch Created 8 years, 4 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
OLDNEW
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/app_list/app_list_bubble_border.h" 5 #include "ui/app_list/app_list_bubble_border.h"
6 6
7 #include "third_party/skia/include/core/SkPath.h" 7 #include "third_party/skia/include/core/SkPath.h"
8 #include "third_party/skia/include/core/SkPaint.h" 8 #include "third_party/skia/include/core/SkPaint.h"
9 #include "third_party/skia/include/effects/SkGradientShader.h" 9 #include "third_party/skia/include/effects/SkGradientShader.h"
10 #include "ui/gfx/canvas.h" 10 #include "ui/gfx/canvas.h"
11 #include "ui/gfx/path.h" 11 #include "ui/gfx/path.h"
12 #include "ui/gfx/skia_util.h" 12 #include "ui/gfx/skia_util.h"
13 13
14 namespace { 14 namespace {
15 15
16 // Bubble border corner radius.
17 const int kCornerRadius = 2;
18
19 // Arrow width and height.
20 const int kArrowHeight = 10;
21 const int kArrowWidth = 20;
22
23 // Bubble border color and width.
24 const SkColor kBorderColor = SkColorSetARGB(0x26, 0, 0, 0);
25 const int kBorderSize = 1;
26
27 const SkColor kSearchBoxBackground = SK_ColorWHITE; 16 const SkColor kSearchBoxBackground = SK_ColorWHITE;
28 const SkColor kContentsBackground = SkColorSetRGB(0xFC, 0xFC, 0xFC); 17 const SkColor kContentsBackground = SkColorSetRGB(0xFC, 0xFC, 0xFC);
29 18
30 // Colors and sizes of top separator between searchbox and grid view. 19 // Colors and sizes of top separator between searchbox and grid view.
31 const SkColor kTopSeparatorColor = SkColorSetRGB(0xF0, 0xF0, 0xF0); 20 const SkColor kTopSeparatorColor = SkColorSetRGB(0xF0, 0xF0, 0xF0);
32 const int kTopSeparatorSize = 1; 21 const int kTopSeparatorSize = 1;
33 22
34 // Builds a bubble shape for given |bounds|.
35 void BuildShape(const gfx::Rect& bounds,
36 views::BubbleBorder::ArrowLocation arrow_location,
37 SkScalar arrow_offset,
38 SkScalar padding,
39 SkPath* path) {
40 const SkScalar corner_radius = SkIntToScalar(kCornerRadius);
41
42 const SkScalar left = SkIntToScalar(bounds.x()) + padding;
43 const SkScalar top = SkIntToScalar(bounds.y()) + padding;
44 const SkScalar right = SkIntToScalar(bounds.right()) - padding;
45 const SkScalar bottom = SkIntToScalar(bounds.bottom()) - padding;
46
47 const SkScalar center_x = SkIntToScalar((bounds.x() + bounds.right()) / 2);
48 const SkScalar center_y = SkIntToScalar((bounds.y() + bounds.bottom()) / 2);
49
50 const SkScalar half_arrow_width = (SkIntToScalar(kArrowWidth) - padding) / 2;
51 const SkScalar arrow_height = SkIntToScalar(kArrowHeight) - padding;
52
53 path->reset();
54 path->incReserve(12);
55
56 switch (arrow_location) {
57 case views::BubbleBorder::TOP_LEFT:
58 case views::BubbleBorder::TOP_RIGHT:
59 path->moveTo(center_x, bottom);
60 path->arcTo(right, bottom, right, center_y, corner_radius);
61 path->arcTo(right, top, center_x - half_arrow_width, top,
62 corner_radius);
63 path->lineTo(center_x + arrow_offset + half_arrow_width, top);
64 path->lineTo(center_x + arrow_offset, top - arrow_height);
65 path->lineTo(center_x + arrow_offset - half_arrow_width, top);
66 path->arcTo(left, top, left, center_y, corner_radius);
67 path->arcTo(left, bottom, center_x, bottom, corner_radius);
68 break;
69 case views::BubbleBorder::BOTTOM_LEFT:
70 case views::BubbleBorder::BOTTOM_RIGHT:
71 path->moveTo(center_x, top);
72 path->arcTo(left, top, left, center_y, corner_radius);
73 path->arcTo(left, bottom, center_x - half_arrow_width, bottom,
74 corner_radius);
75 path->lineTo(center_x + arrow_offset - half_arrow_width, bottom);
76 path->lineTo(center_x + arrow_offset, bottom + arrow_height);
77 path->lineTo(center_x + arrow_offset + half_arrow_width, bottom);
78 path->arcTo(right, bottom, right, center_y, corner_radius);
79 path->arcTo(right, top, center_x, top, corner_radius);
80 break;
81 case views::BubbleBorder::LEFT_TOP:
82 case views::BubbleBorder::LEFT_BOTTOM:
83 path->moveTo(right, center_y);
84 path->arcTo(right, top, center_x, top, corner_radius);
85 path->arcTo(left, top, left, center_y + arrow_offset - half_arrow_width,
86 corner_radius);
87 path->lineTo(left, center_y + arrow_offset - half_arrow_width);
88 path->lineTo(left - arrow_height, center_y + arrow_offset);
89 path->lineTo(left, center_y + arrow_offset + half_arrow_width);
90 path->arcTo(left, bottom, center_x, bottom, corner_radius);
91 path->arcTo(right, bottom, right, center_y, corner_radius);
92 break;
93 case views::BubbleBorder::RIGHT_TOP:
94 case views::BubbleBorder::RIGHT_BOTTOM:
95 path->moveTo(left, center_y);
96 path->arcTo(left, bottom, center_x, bottom, corner_radius);
97 path->arcTo(right, bottom,
98 right, center_y + arrow_offset + half_arrow_width,
99 corner_radius);
100 path->lineTo(right, center_y + arrow_offset + half_arrow_width);
101 path->lineTo(right + arrow_height, center_y + arrow_offset);
102 path->lineTo(right, center_y + arrow_offset - half_arrow_width);
103 path->arcTo(right, top, center_x, top, corner_radius);
104 path->arcTo(left, top, left, center_y, corner_radius);
105 break;
106 default:
107 // No arrows.
108 path->addRoundRect(gfx::RectToSkRect(bounds),
109 corner_radius,
110 corner_radius);
111 break;
112 }
113
114 path->close();
115 }
116
117 } // namespace 23 } // namespace
118 24
119 namespace app_list { 25 namespace app_list {
120 26
121 AppListBubbleBorder::AppListBubbleBorder(views::View* app_list_view, 27 AppListBubbleBorder::AppListBubbleBorder(views::View* app_list_view,
122 views::View* search_box_view) 28 views::View* search_box_view)
123 : views::BubbleBorder(views::BubbleBorder::BOTTOM_RIGHT, 29 : views::ImagelessBubbleBorder(views::BubbleBorder::BOTTOM_RIGHT),
124 views::BubbleBorder::NO_SHADOW),
125 app_list_view_(app_list_view), 30 app_list_view_(app_list_view),
126 search_box_view_(search_box_view) { 31 search_box_view_(search_box_view) {
127 const gfx::ShadowValue kShadows[] = {
128 // Offset (0, 5), blur=30, color=0.36 black
129 gfx::ShadowValue(gfx::Point(0, 5), 30, SkColorSetARGB(0x72, 0, 0, 0)),
130 };
131 shadows_.assign(kShadows, kShadows + arraysize(kShadows));
132 } 32 }
133 33
134 AppListBubbleBorder::~AppListBubbleBorder() { 34 AppListBubbleBorder::~AppListBubbleBorder() {
135 } 35 }
136 36
137 bool AppListBubbleBorder::ArrowAtTopOrBottom() const {
138 return arrow_location() == views::BubbleBorder::TOP_LEFT ||
139 arrow_location() == views::BubbleBorder::TOP_RIGHT ||
140 arrow_location() == views::BubbleBorder::BOTTOM_LEFT ||
141 arrow_location() == views::BubbleBorder::BOTTOM_RIGHT;
142 }
143
144 bool AppListBubbleBorder::ArrowOnLeftOrRight() const {
145 return arrow_location() == views::BubbleBorder::LEFT_TOP ||
146 arrow_location() == views::BubbleBorder::LEFT_BOTTOM ||
147 arrow_location() == views::BubbleBorder::RIGHT_TOP ||
148 arrow_location() == views::BubbleBorder::RIGHT_BOTTOM;
149 }
150
151 void AppListBubbleBorder::GetMask(const gfx::Rect& bounds,
152 gfx::Path* mask) const {
153 gfx::Insets insets;
154 GetInsets(&insets);
155
156 gfx::Rect content_bounds(bounds);
157 content_bounds.Inset(insets);
158
159 BuildShape(content_bounds,
160 arrow_location(),
161 SkIntToScalar(GetArrowOffset()),
162 SkIntToScalar(kBorderSize),
163 mask);
164 }
165
166 int AppListBubbleBorder::GetArrowOffset() const {
167 if (ArrowAtTopOrBottom()) {
168 // Picks x offset and moves bubble arrow in the opposite direction.
169 // i.e. If bubble bounds is moved to right (positive offset), we need to
170 // move arrow to left so that it points to the same position.
171 return -offset_.x();
172 } else if (ArrowOnLeftOrRight()) {
173 // Picks y offset and moves bubble arrow in the opposite direction.
174 return -offset_.y();
175 }
176
177 // Other style does not have an arrow, so return 0.
178 return 0;
179 }
180
181 void AppListBubbleBorder::PaintBackground(gfx::Canvas* canvas, 37 void AppListBubbleBorder::PaintBackground(gfx::Canvas* canvas,
182 const gfx::Rect& bounds) const { 38 const gfx::Rect& bounds) const {
183 const gfx::Rect search_box_view_bounds = 39 const gfx::Rect search_box_view_bounds =
184 app_list_view_->ConvertRectToWidget(search_box_view_->bounds()); 40 app_list_view_->ConvertRectToWidget(search_box_view_->bounds());
185 gfx::Rect search_box_rect(bounds.x(), 41 gfx::Rect search_box_rect(bounds.x(),
186 bounds.y(), 42 bounds.y(),
187 bounds.width(), 43 bounds.width(),
188 search_box_view_bounds.bottom() - bounds.y()); 44 search_box_view_bounds.bottom() - bounds.y());
189 45
190 SkPaint paint; 46 SkPaint paint;
191 paint.setStyle(SkPaint::kFill_Style); 47 paint.setStyle(SkPaint::kFill_Style);
192 paint.setColor(kSearchBoxBackground); 48 paint.setColor(kSearchBoxBackground);
193 canvas->DrawRect(search_box_rect, paint); 49 canvas->DrawRect(search_box_rect, paint);
194 50
195 gfx::Rect seperator_rect(search_box_rect); 51 gfx::Rect seperator_rect(search_box_rect);
196 seperator_rect.set_y(seperator_rect.bottom()); 52 seperator_rect.set_y(seperator_rect.bottom());
197 seperator_rect.set_height(kTopSeparatorSize); 53 seperator_rect.set_height(kTopSeparatorSize);
198 canvas->FillRect(seperator_rect, kTopSeparatorColor); 54 canvas->FillRect(seperator_rect, kTopSeparatorColor);
199 55
200 gfx::Rect contents_rect(bounds.x(), 56 gfx::Rect contents_rect(bounds.x(),
201 seperator_rect.bottom(), 57 seperator_rect.bottom(),
202 bounds.width(), 58 bounds.width(),
203 bounds.bottom() - seperator_rect.bottom()); 59 bounds.bottom() - seperator_rect.bottom());
204 60
205 paint.setColor(kContentsBackground); 61 paint.setColor(kContentsBackground);
206 canvas->DrawRect(contents_rect, paint); 62 canvas->DrawRect(contents_rect, paint);
207 } 63 }
208 64
209 void AppListBubbleBorder::GetInsets(gfx::Insets* insets) const {
210 // Negate to change from outer margin to inner padding.
211 gfx::Insets shadow_padding(-gfx::ShadowValue::GetMargin(shadows_));
212
213 if (arrow_location() == views::BubbleBorder::TOP_LEFT ||
214 arrow_location() == views::BubbleBorder::TOP_RIGHT) {
215 // Arrow at top.
216 insets->Set(shadow_padding.top() + kArrowHeight,
217 shadow_padding.left(),
218 shadow_padding.bottom(),
219 shadow_padding.right());
220 } else if (arrow_location() == views::BubbleBorder::BOTTOM_LEFT ||
221 arrow_location() == views::BubbleBorder::BOTTOM_RIGHT) {
222 // Arrow at bottom.
223 insets->Set(shadow_padding.top(),
224 shadow_padding.left(),
225 shadow_padding.bottom() + kArrowHeight,
226 shadow_padding.right());
227 } else if (arrow_location() == views::BubbleBorder::LEFT_TOP ||
228 arrow_location() == views::BubbleBorder::LEFT_BOTTOM) {
229 // Arrow on left.
230 insets->Set(shadow_padding.top(),
231 shadow_padding.left() + kArrowHeight,
232 shadow_padding.bottom(),
233 shadow_padding.right());
234 } else if (arrow_location() == views::BubbleBorder::RIGHT_TOP ||
235 arrow_location() == views::BubbleBorder::RIGHT_BOTTOM) {
236 // Arrow on right.
237 insets->Set(shadow_padding.top(),
238 shadow_padding.left(),
239 shadow_padding.bottom(),
240 shadow_padding.right() + kArrowHeight);
241 }
242 }
243
244 gfx::Rect AppListBubbleBorder::GetBounds(
245 const gfx::Rect& position_relative_to,
246 const gfx::Size& contents_size) const {
247 gfx::Size border_size(contents_size);
248 gfx::Insets insets;
249 GetInsets(&insets);
250 border_size.Enlarge(insets.width(), insets.height());
251
252 // Negate to change from outer margin to inner padding.
253 gfx::Insets shadow_padding(-gfx::ShadowValue::GetMargin(shadows_));
254
255 // Anchor center that arrow aligns with.
256 const int anchor_center_x =
257 (position_relative_to.x() + position_relative_to.right()) / 2;
258 const int anchor_center_y =
259 (position_relative_to.y() + position_relative_to.bottom()) / 2;
260
261 // Arrow position relative to top-left of bubble. |arrow_tip_x| is used for
262 // arrow at the top or bottom and |arrow_tip_y| is used for arrow on left or
263 // right. The 1px offset for |arrow_tip_y| is needed because the app list grid
264 // icon start at a different position (1px earlier) compared with bottom
265 // launcher bar.
266 // TODO(xiyuan): Remove 1px offset when app list icon image asset is updated.
267 int arrow_tip_x = insets.left() + contents_size.width() / 2 +
268 GetArrowOffset();
269 int arrow_tip_y = insets.top() + contents_size.height() / 2 +
270 GetArrowOffset() + 1;
271
272 if (arrow_location() == views::BubbleBorder::TOP_LEFT ||
273 arrow_location() == views::BubbleBorder::TOP_RIGHT) {
274 // Arrow at top.
275 return gfx::Rect(
276 gfx::Point(anchor_center_x - arrow_tip_x,
277 position_relative_to.bottom() - shadow_padding.top() -
278 kArrowHeight),
279 border_size);
280 } else if (arrow_location() == views::BubbleBorder::BOTTOM_LEFT ||
281 arrow_location() == views::BubbleBorder::BOTTOM_RIGHT) {
282 // Arrow at bottom.
283 return gfx::Rect(
284 gfx::Point(anchor_center_x - arrow_tip_x,
285 position_relative_to.y() - border_size.height() +
286 shadow_padding.bottom() + kArrowHeight),
287 border_size);
288 } else if (arrow_location() == views::BubbleBorder::LEFT_TOP ||
289 arrow_location() == views::BubbleBorder::LEFT_BOTTOM) {
290 // Arrow on left.
291 return gfx::Rect(
292 gfx::Point(position_relative_to.right() - shadow_padding.left() -
293 kArrowHeight,
294 anchor_center_y - arrow_tip_y),
295 border_size);
296 } else if (arrow_location() == views::BubbleBorder::RIGHT_TOP ||
297 arrow_location() == views::BubbleBorder::RIGHT_BOTTOM) {
298 // Arrow on right.
299 return gfx::Rect(
300 gfx::Point(position_relative_to.x() - border_size.width() +
301 shadow_padding.right() + kArrowHeight,
302 anchor_center_y - arrow_tip_y),
303 border_size);
304 }
305
306 // No arrow bubble, center align with anchor.
307 return position_relative_to.Center(border_size);
308 }
309
310 void AppListBubbleBorder::Paint(const views::View& view,
311 gfx::Canvas* canvas) const {
312 gfx::Insets insets;
313 GetInsets(&insets);
314
315 gfx::Rect content_bounds = view.bounds();
316 content_bounds.Inset(insets);
317
318 SkPath path;
319 // Pads with 0.5 pixel since anti alias is used.
320 BuildShape(content_bounds,
321 arrow_location(),
322 SkIntToScalar(GetArrowOffset()),
323 SkDoubleToScalar(0.5),
324 &path);
325
326 // Draw border and shadow. Note fill is needed to generate enough shadow.
327 SkPaint paint;
328 paint.setAntiAlias(true);
329 paint.setStyle(SkPaint::kStrokeAndFill_Style);
330 paint.setStrokeWidth(SkIntToScalar(kBorderSize));
331 paint.setColor(kBorderColor);
332 SkSafeUnref(paint.setLooper(gfx::CreateShadowDrawLooper(shadows_)));
333 canvas->DrawPath(path, paint);
334
335 // Pads with kBoprderSize pixels to leave space for border lines.
336 BuildShape(content_bounds,
337 arrow_location(),
338 SkIntToScalar(GetArrowOffset()),
339 SkIntToScalar(kBorderSize),
340 &path);
341 canvas->Save();
342 canvas->ClipPath(path);
343
344 // Use full bounds so that arrow is also painted.
345 const gfx::Rect& bounds = view.bounds();
346 PaintBackground(canvas, bounds);
347
348 canvas->Restore();
349 }
350
351 } // namespace app_list 65 } // namespace app_list
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698