OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |