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

Side by Side Diff: ui/views/bubble/bubble_border.cc

Issue 454173002: Fixed BubbleBorder sizing problems & added unit tests. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Combined test fixtures and rewrote tests to be more easily maintained. Created 6 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
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/views/bubble/bubble_border.h" 5 #include "ui/views/bubble/bubble_border.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h" 10 #include "base/memory/scoped_ptr.h"
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
65 right_arrow = *rb.GetImageSkiaNamed(arrow_image_ids[2]); 65 right_arrow = *rb.GetImageSkiaNamed(arrow_image_ids[2]);
66 bottom_arrow = *rb.GetImageSkiaNamed(arrow_image_ids[3]); 66 bottom_arrow = *rb.GetImageSkiaNamed(arrow_image_ids[3]);
67 arrow_thickness = top_arrow.height(); 67 arrow_thickness = top_arrow.height();
68 } 68 }
69 } 69 }
70 70
71 } // namespace internal 71 } // namespace internal
72 72
73 namespace { 73 namespace {
74 74
75 // The border and arrow stroke size used in image assets, in pixels.
76 const int kStroke = 1;
77
78 // Bubble border and arrow image resource ids. They don't use the IMAGE_GRID 75 // Bubble border and arrow image resource ids. They don't use the IMAGE_GRID
79 // macro because there is no center image. 76 // macro because there is no center image.
80 const int kNoShadowImages[] = { 77 const int kNoShadowImages[] = {
81 IDR_BUBBLE_TL, IDR_BUBBLE_T, IDR_BUBBLE_TR, 78 IDR_BUBBLE_TL, IDR_BUBBLE_T, IDR_BUBBLE_TR,
82 IDR_BUBBLE_L, 0, IDR_BUBBLE_R, 79 IDR_BUBBLE_L, 0, IDR_BUBBLE_R,
83 IDR_BUBBLE_BL, IDR_BUBBLE_B, IDR_BUBBLE_BR }; 80 IDR_BUBBLE_BL, IDR_BUBBLE_B, IDR_BUBBLE_BR };
84 const int kNoShadowArrows[] = { 81 const int kNoShadowArrows[] = {
85 IDR_BUBBLE_L_ARROW, IDR_BUBBLE_T_ARROW, 82 IDR_BUBBLE_L_ARROW, IDR_BUBBLE_T_ARROW,
86 IDR_BUBBLE_R_ARROW, IDR_BUBBLE_B_ARROW, }; 83 IDR_BUBBLE_R_ARROW, IDR_BUBBLE_B_ARROW, };
87 84
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
143 case BubbleBorder::SHADOW_COUNT: 140 case BubbleBorder::SHADOW_COUNT:
144 NOTREACHED(); 141 NOTREACHED();
145 break; 142 break;
146 } 143 }
147 144
148 return set; 145 return set;
149 } 146 }
150 147
151 } // namespace 148 } // namespace
152 149
150 const int BubbleBorder::kStroke = 1;
151
153 BubbleBorder::BubbleBorder(Arrow arrow, Shadow shadow, SkColor color) 152 BubbleBorder::BubbleBorder(Arrow arrow, Shadow shadow, SkColor color)
154 : arrow_(arrow), 153 : arrow_(arrow),
155 arrow_offset_(0), 154 arrow_offset_(0),
156 arrow_paint_type_(PAINT_NORMAL), 155 arrow_paint_type_(PAINT_NORMAL),
157 alignment_(ALIGN_ARROW_TO_MID_ANCHOR), 156 alignment_(ALIGN_ARROW_TO_MID_ANCHOR),
158 shadow_(shadow), 157 shadow_(shadow),
159 background_color_(color), 158 background_color_(color),
160 use_theme_background_color_(false) { 159 use_theme_background_color_(false) {
161 DCHECK(shadow < SHADOW_COUNT); 160 DCHECK(shadow < SHADOW_COUNT);
162 images_ = GetBorderImages(shadow); 161 images_ = GetBorderImages(shadow);
163 } 162 }
164 163
165 BubbleBorder::~BubbleBorder() {} 164 BubbleBorder::~BubbleBorder() {}
166 165
167 gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect, 166 gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect,
168 const gfx::Size& contents_size) const { 167 const gfx::Size& contents_size) const {
169 int x = anchor_rect.x(); 168 int x = anchor_rect.x();
170 int y = anchor_rect.y(); 169 int y = anchor_rect.y();
171 int w = anchor_rect.width(); 170 int w = anchor_rect.width();
172 int h = anchor_rect.height(); 171 int h = anchor_rect.height();
173 const gfx::Size size(GetSizeForContentsSize(contents_size)); 172 const gfx::Size size(GetSizeForContentsSize(contents_size));
174 const int arrow_offset = GetArrowOffset(size); 173 const int arrow_offset = GetArrowOffset(size);
175 const int arrow_size = 174 const int arrow_size = GetArrowSize();
176 images_->arrow_interior_thickness + kStroke - images_->arrow_thickness;
177 const bool mid_anchor = alignment_ == ALIGN_ARROW_TO_MID_ANCHOR; 175 const bool mid_anchor = alignment_ == ALIGN_ARROW_TO_MID_ANCHOR;
178 176
179 // Calculate the bubble coordinates based on the border and arrow settings. 177 // Calculate the bubble coordinates based on the border and arrow settings.
180 if (is_arrow_on_horizontal(arrow_)) { 178 if (is_arrow_on_horizontal(arrow_)) {
181 if (is_arrow_on_left(arrow_)) { 179 if (is_arrow_on_left(arrow_)) {
182 x += mid_anchor ? w / 2 - arrow_offset : kStroke - GetBorderThickness(); 180 x += mid_anchor ?
181 w / 2 - arrow_offset :
182 kStroke - GetBorderExteriorThickness();
183 } else if (is_arrow_at_center(arrow_)) { 183 } else if (is_arrow_at_center(arrow_)) {
184 x += w / 2 - arrow_offset; 184 x += w / 2 - arrow_offset;
185 } else { 185 } else {
186 x += mid_anchor ? w / 2 + arrow_offset - size.width() : 186 x += mid_anchor ?
187 w - size.width() + GetBorderThickness() - kStroke; 187 w / 2 + arrow_offset - size.width() :
188 w - size.width() + GetBorderExteriorThickness() - kStroke;
188 } 189 }
189 y += is_arrow_on_top(arrow_) ? h + arrow_size : -arrow_size - size.height(); 190 y += is_arrow_on_top(arrow_) ? h + arrow_size : -arrow_size - size.height();
190 } else if (has_arrow(arrow_)) { 191 } else if (has_arrow(arrow_)) {
191 x += is_arrow_on_left(arrow_) ? w + arrow_size : -arrow_size - size.width(); 192 x += is_arrow_on_left(arrow_) ? w + arrow_size : -arrow_size - size.width();
192 if (is_arrow_on_top(arrow_)) { 193 if (is_arrow_on_top(arrow_)) {
193 y += mid_anchor ? h / 2 - arrow_offset : kStroke - GetBorderThickness(); 194 y += mid_anchor ?
195 h / 2 - arrow_offset :
196 kStroke - GetBorderExteriorThickness();
194 } else if (is_arrow_at_center(arrow_)) { 197 } else if (is_arrow_at_center(arrow_)) {
195 y += h / 2 - arrow_offset; 198 y += h / 2 - arrow_offset;
196 } else { 199 } else {
197 y += mid_anchor ? h / 2 + arrow_offset - size.height() : 200 y += mid_anchor ?
198 h - size.height() + GetBorderThickness() - kStroke; 201 h / 2 + arrow_offset - size.height() :
202 h - size.height() + GetBorderExteriorThickness() - kStroke;
199 } 203 }
200 } else { 204 } else {
201 x += (w - size.width()) / 2; 205 x += (w - size.width()) / 2;
202 y += (arrow_ == NONE) ? h : (h - size.height()) / 2; 206 y += (arrow_ == NONE) ? h : (h - size.height()) / 2;
203 } 207 }
204 208
205 return gfx::Rect(x, y, size.width(), size.height()); 209 return gfx::Rect(x, y, size.width(), size.height());
206 } 210 }
207 211
212 int BubbleBorder::GetArrowThickness() const {
213 return images_->arrow_thickness;
214 }
215
216 int BubbleBorder::GetTopArrowWidth() const {
217 return images_->top_arrow.width();
218 }
219
208 int BubbleBorder::GetBorderThickness() const { 220 int BubbleBorder::GetBorderThickness() const {
221 return images_->border_thickness;
222 }
223
224 int BubbleBorder::GetBorderInteriorThickness() const {
225 return images_->border_interior_thickness;
226 }
227
228 int BubbleBorder::GetBorderExteriorThickness() const {
209 return images_->border_thickness - images_->border_interior_thickness; 229 return images_->border_thickness - images_->border_interior_thickness;
210 } 230 }
211 231
212 int BubbleBorder::GetBorderCornerRadius() const { 232 int BubbleBorder::GetBorderCornerRadius() const {
213 return images_->corner_radius; 233 return images_->corner_radius;
214 } 234 }
215 235
216 int BubbleBorder::GetArrowOffset(const gfx::Size& border_size) const { 236 int BubbleBorder::GetArrowOffset(const gfx::Size& border_size) const {
217 const int edge_length = is_arrow_on_horizontal(arrow_) ? 237 const int edge_length = is_arrow_on_horizontal(arrow_) ?
218 border_size.width() : border_size.height(); 238 border_size.width() : border_size.height();
219 if (is_arrow_at_center(arrow_) && arrow_offset_ == 0) 239 if (is_arrow_at_center(arrow_) && arrow_offset_ == 0)
220 return edge_length / 2; 240 return edge_length / 2;
221 241
222 // Calculate the minimum offset to not overlap arrow and corner images. 242 // Calculate the minimum offset to not overlap arrow and corner images.
223 const int min = images_->border_thickness + (images_->top_arrow.width() / 2); 243 const int min = images_->border_thickness + (images_->top_arrow.width() / 2);
224 // Ensure the returned value will not cause image overlap, if possible. 244 // Ensure the returned value will not cause image overlap, if possible.
225 return std::max(min, std::min(arrow_offset_, edge_length - min)); 245 return std::max(min, std::min(arrow_offset_, edge_length - min));
226 } 246 }
227 247
248 int BubbleBorder::GetArrowSize() const {
249 return images_->arrow_interior_thickness + kStroke - images_->arrow_thickness;
250 }
251
228 void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) { 252 void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) {
229 gfx::Rect bounds(view.GetContentsBounds()); 253 gfx::Rect bounds(view.GetContentsBounds());
230 bounds.Inset(-GetBorderThickness(), -GetBorderThickness()); 254 bounds.Inset(-GetBorderExteriorThickness(), -GetBorderExteriorThickness());
231 const gfx::Rect arrow_bounds = GetArrowRect(view.GetLocalBounds()); 255 const gfx::Rect arrow_bounds = GetArrowRect(view.GetLocalBounds());
232 if (arrow_bounds.IsEmpty()) { 256 if (arrow_bounds.IsEmpty()) {
233 Painter::PaintPainterAt(canvas, images_->border_painter.get(), bounds); 257 Painter::PaintPainterAt(canvas, images_->border_painter.get(), bounds);
234 return; 258 return;
235 } 259 }
236 260
237 // Clip the arrow bounds out to avoid painting the overlapping edge area. 261 // Clip the arrow bounds out to avoid painting the overlapping edge area.
238 canvas->Save(); 262 canvas->Save();
239 SkRect arrow_rect(gfx::RectToSkRect(arrow_bounds)); 263 SkRect arrow_rect(gfx::RectToSkRect(arrow_bounds));
240 canvas->sk_canvas()->clipRect(arrow_rect, SkRegion::kDifference_Op); 264 canvas->sk_canvas()->clipRect(arrow_rect, SkRegion::kDifference_Op);
241 Painter::PaintPainterAt(canvas, images_->border_painter.get(), bounds); 265 Painter::PaintPainterAt(canvas, images_->border_painter.get(), bounds);
242 canvas->Restore(); 266 canvas->Restore();
243 267
244 DrawArrow(canvas, arrow_bounds); 268 DrawArrow(canvas, arrow_bounds);
245 } 269 }
246 270
247 gfx::Insets BubbleBorder::GetInsets() const { 271 gfx::Insets BubbleBorder::GetInsets() const {
248 // The insets contain the stroke and shadow pixels outside the bubble fill. 272 // The insets contain the stroke and shadow pixels outside the bubble fill.
249 const int inset = GetBorderThickness(); 273 const int inset = GetBorderExteriorThickness();
250 if ((arrow_paint_type_ == PAINT_NONE) || !has_arrow(arrow_)) 274 if ((arrow_paint_type_ == PAINT_NONE) || !has_arrow(arrow_))
251 return gfx::Insets(inset, inset, inset, inset); 275 return gfx::Insets(inset, inset, inset, inset);
252 276
253 int first_inset = inset; 277 int first_inset = inset;
254 int second_inset = std::max(inset, images_->arrow_thickness); 278 int second_inset = std::max(inset, images_->arrow_thickness);
255 if (is_arrow_on_horizontal(arrow_) ? 279 if (is_arrow_on_horizontal(arrow_) ?
256 is_arrow_on_top(arrow_) : is_arrow_on_left(arrow_)) 280 is_arrow_on_top(arrow_) : is_arrow_on_left(arrow_))
257 std::swap(first_inset, second_inset); 281 std::swap(first_inset, second_inset);
258 return is_arrow_on_horizontal(arrow_) ? 282 return is_arrow_on_horizontal(arrow_) ?
259 gfx::Insets(first_inset, inset, second_inset, inset) : 283 gfx::Insets(first_inset, inset, second_inset, inset) :
(...skipping 11 matching lines...) Expand all
271 const gfx::Insets insets = GetInsets(); 295 const gfx::Insets insets = GetInsets();
272 size.Enlarge(insets.width(), insets.height()); 296 size.Enlarge(insets.width(), insets.height());
273 297
274 // Ensure the bubble is large enough to not overlap border and arrow images. 298 // Ensure the bubble is large enough to not overlap border and arrow images.
275 const int min = 2 * images_->border_thickness; 299 const int min = 2 * images_->border_thickness;
276 const int min_with_arrow_width = min + images_->top_arrow.width(); 300 const int min_with_arrow_width = min + images_->top_arrow.width();
277 const int min_with_arrow_thickness = images_->border_thickness + 301 const int min_with_arrow_thickness = images_->border_thickness +
278 std::max(images_->arrow_thickness + images_->border_interior_thickness, 302 std::max(images_->arrow_thickness + images_->border_interior_thickness,
279 images_->border_thickness); 303 images_->border_thickness);
280 // Only take arrow image sizes into account when the bubble tip is shown. 304 // Only take arrow image sizes into account when the bubble tip is shown.
281 if (arrow_paint_type_ == PAINT_TRANSPARENT || !has_arrow(arrow_)) 305 if (arrow_paint_type_ == PAINT_NONE || !has_arrow(arrow_))
282 size.SetToMax(gfx::Size(min, min)); 306 size.SetToMax(gfx::Size(min, min));
283 else if (is_arrow_on_horizontal(arrow_)) 307 else if (is_arrow_on_horizontal(arrow_))
284 size.SetToMax(gfx::Size(min_with_arrow_width, min_with_arrow_thickness)); 308 size.SetToMax(gfx::Size(min_with_arrow_width, min_with_arrow_thickness));
285 else 309 else
286 size.SetToMax(gfx::Size(min_with_arrow_thickness, min_with_arrow_width)); 310 size.SetToMax(gfx::Size(min_with_arrow_thickness, min_with_arrow_width));
287 return size; 311 return size;
288 } 312 }
289 313
290 gfx::ImageSkia* BubbleBorder::GetArrowImage() const { 314 gfx::ImageSkia* BubbleBorder::GetArrowImage() const {
291 if (!has_arrow(arrow_)) 315 if (!has_arrow(arrow_))
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
372 SkPath path; 396 SkPath path;
373 gfx::Rect bounds(view->GetLocalBounds()); 397 gfx::Rect bounds(view->GetLocalBounds());
374 bounds.Inset(border_->GetInsets()); 398 bounds.Inset(border_->GetInsets());
375 399
376 SkScalar radius = SkIntToScalar(border_->GetBorderCornerRadius()); 400 SkScalar radius = SkIntToScalar(border_->GetBorderCornerRadius());
377 path.addRoundRect(gfx::RectToSkRect(bounds), radius, radius); 401 path.addRoundRect(gfx::RectToSkRect(bounds), radius, radius);
378 canvas->DrawPath(path, paint); 402 canvas->DrawPath(path, paint);
379 } 403 }
380 404
381 } // namespace views 405 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698