summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ash/launcher/launcher_tooltip_manager.cc58
-rw-r--r--ash/launcher/overflow_bubble.cc8
-rw-r--r--ash/wm/app_list_controller.cc8
-rw-r--r--ash/wm/maximize_bubble_controller.cc4
-rw-r--r--ui/app_list/app_list.gyp4
-rw-r--r--ui/app_list/app_list_background.cc (renamed from ui/app_list/app_list_bubble_border.cc)33
-rw-r--r--ui/app_list/app_list_background.h37
-rw-r--r--ui/app_list/app_list_bubble_border.h37
-rw-r--r--ui/app_list/app_list_view.cc51
-rw-r--r--ui/app_list/app_list_view.h5
-rw-r--r--ui/resources/default_100_percent/common/window_bubble_shadow_spike_big_left.pngbin459 -> 1502 bytes
-rw-r--r--ui/resources/default_100_percent/common/window_bubble_shadow_spike_big_right.pngbin481 -> 1492 bytes
-rw-r--r--ui/resources/default_100_percent/common/window_bubble_shadow_spike_small_left.pngbin285 -> 1237 bytes
-rw-r--r--ui/resources/default_100_percent/common/window_bubble_shadow_spike_small_right.pngbin288 -> 1223 bytes
-rw-r--r--ui/resources/default_200_percent/common/window_bubble_shadow_spike_big_left.pngbin1081 -> 2255 bytes
-rw-r--r--ui/resources/default_200_percent/common/window_bubble_shadow_spike_big_right.pngbin1133 -> 2288 bytes
-rw-r--r--ui/resources/default_200_percent/common/window_bubble_shadow_spike_small_left.pngbin680 -> 1719 bytes
-rw-r--r--ui/resources/default_200_percent/common/window_bubble_shadow_spike_small_right.pngbin699 -> 1697 bytes
-rw-r--r--ui/resources/ui_resources.grd24
-rw-r--r--ui/views/bubble/bubble_border.cc462
-rw-r--r--ui/views/bubble/bubble_border.h78
-rw-r--r--ui/views/bubble/bubble_border_2.cc399
-rw-r--r--ui/views/bubble/bubble_border_2.h91
-rw-r--r--ui/views/bubble/bubble_border_unittest.cc205
-rw-r--r--ui/views/bubble/bubble_delegate.cc12
-rw-r--r--ui/views/bubble/bubble_delegate.h18
-rw-r--r--ui/views/bubble/bubble_frame_view.cc55
-rw-r--r--ui/views/bubble/bubble_frame_view.h9
-rw-r--r--ui/views/bubble/bubble_frame_view_unittest.cc157
-rw-r--r--ui/views/views.gyp3
30 files changed, 883 insertions, 875 deletions
diff --git a/ash/launcher/launcher_tooltip_manager.cc b/ash/launcher/launcher_tooltip_manager.cc
index d7c07a6..c817e8f 100644
--- a/ash/launcher/launcher_tooltip_manager.cc
+++ b/ash/launcher/launcher_tooltip_manager.cc
@@ -17,7 +17,6 @@
#include "ui/base/events/event.h"
#include "ui/base/events/event_constants.h"
#include "ui/gfx/insets.h"
-#include "ui/views/bubble/bubble_border_2.h"
#include "ui/views/bubble/bubble_delegate.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/controls/label.h"
@@ -37,21 +36,17 @@ const SkColor kTooltipTextColor = SkColorSetRGB(0x22, 0x22, 0x22);
// ash/tooltip/tooltip_controller.cc
const int kTooltipMaxWidth = 250;
-// Bubble border metrics
-const int kArrowHeight = 7;
-const int kArrowWidth = 15;
-const int kShadowWidth = 8;
// The distance between the arrow tip and edge of the anchor view.
const int kArrowOffset = 10;
views::BubbleBorder::ArrowLocation GetArrowLocation(ShelfAlignment alignment) {
switch (alignment) {
case SHELF_ALIGNMENT_LEFT:
- return views::BubbleBorder::LEFT_BOTTOM;
+ return views::BubbleBorder::LEFT_CENTER;
case SHELF_ALIGNMENT_RIGHT:
- return views::BubbleBorder::RIGHT_BOTTOM;
+ return views::BubbleBorder::RIGHT_CENTER;
case SHELF_ALIGNMENT_BOTTOM:
- return views::BubbleBorder::BOTTOM_RIGHT;
+ return views::BubbleBorder::BOTTOM_CENTER;
}
return views::BubbleBorder::NONE;
@@ -70,9 +65,6 @@ class LauncherTooltipManager::LauncherTooltipBubble
void Close();
private:
- // Overridden from views::BubbleDelegateView:
- virtual gfx::Rect GetBubbleBounds() OVERRIDE;
-
// views::WidgetDelegate overrides:
virtual void WindowClosing() OVERRIDE;
@@ -81,7 +73,6 @@ class LauncherTooltipManager::LauncherTooltipBubble
LauncherTooltipManager* host_;
views::Label* label_;
- views::BubbleBorder2* bubble_border_;
DISALLOW_COPY_AND_ASSIGN(LauncherTooltipBubble);
};
@@ -91,14 +82,16 @@ LauncherTooltipManager::LauncherTooltipBubble::LauncherTooltipBubble(
views::BubbleBorder::ArrowLocation arrow_location,
LauncherTooltipManager* host)
: views::BubbleDelegateView(anchor, arrow_location),
- host_(host),
- bubble_border_(NULL) {
+ host_(host) {
+ set_anchor_insets(gfx::Insets(kArrowOffset, kArrowOffset, kArrowOffset,
+ kArrowOffset));
set_close_on_esc(false);
set_close_on_deactivate(false);
set_use_focusless(true);
set_accept_events(false);
set_margins(gfx::Insets(kTooltipTopBottomMargin, kTooltipLeftRightMargin,
kTooltipTopBottomMargin, kTooltipLeftRightMargin));
+ set_shadow(views::BubbleBorder::SMALL_SHADOW);
SetLayoutManager(new views::FillLayout());
// The anchor may not have the widget in tests.
if (anchor->GetWidget() && anchor->GetWidget()->GetNativeView()) {
@@ -113,17 +106,6 @@ LauncherTooltipManager::LauncherTooltipBubble::LauncherTooltipBubble(
label_->SetElideBehavior(views::Label::ELIDE_AT_END);
AddChildView(label_);
views::BubbleDelegateView::CreateBubble(this);
- bubble_border_ = new views::BubbleBorder2(views::BubbleBorder::BOTTOM_RIGHT);
- bubble_border_->SetShadow(gfx::ShadowValue(
- gfx::Point(0, 5), kShadowWidth, SkColorSetARGB(0x72, 0, 0, 0)));
- bubble_border_->set_arrow_width(kArrowWidth);
- bubble_border_->set_arrow_height(kArrowHeight);
- set_anchor_insets(gfx::Insets(kArrowOffset, kArrowOffset, kArrowOffset,
- kArrowOffset));
- GetBubbleFrameView()->SetBubbleBorder(bubble_border_);
-
- // BubbleBorder2 paints its own background.
- GetBubbleFrameView()->set_background(NULL);
}
void LauncherTooltipManager::LauncherTooltipBubble::SetText(
@@ -139,28 +121,6 @@ void LauncherTooltipManager::LauncherTooltipBubble::Close() {
}
}
-gfx::Rect LauncherTooltipManager::LauncherTooltipBubble::GetBubbleBounds() {
- // This happens before replacing the default border.
- if (!bubble_border_)
- return views::BubbleDelegateView::GetBubbleBounds();
-
- const gfx::Rect anchor_rect = GetAnchorRect();
- gfx::Rect bubble_rect = GetBubbleFrameView()->GetUpdatedWindowBounds(
- anchor_rect,
- GetPreferredSize(),
- false /* try_mirroring_arrow */);
-
- const gfx::Point old_offset = bubble_border_->offset();
- bubble_rect = bubble_border_->ComputeOffsetAndUpdateBubbleRect(bubble_rect,
- anchor_rect);
-
- // Repaints border if arrow offset is changed.
- if (bubble_border_->offset() != old_offset)
- GetBubbleFrameView()->SchedulePaint();
-
- return bubble_rect;
-}
-
void LauncherTooltipManager::LauncherTooltipBubble::WindowClosing() {
views::BubbleDelegateView::WindowClosing();
if (host_)
@@ -204,9 +164,9 @@ LauncherTooltipManager::~LauncherTooltipManager() {
void LauncherTooltipManager::ShowDelayed(views::View* anchor,
const string16& text) {
if (view_) {
- if (timer_.get() && timer_->IsRunning())
+ if (timer_.get() && timer_->IsRunning()) {
return;
- else {
+ } else {
CancelHidingAnimation();
Close();
}
diff --git a/ash/launcher/overflow_bubble.cc b/ash/launcher/overflow_bubble.cc
index 0daf312..6489583 100644
--- a/ash/launcher/overflow_bubble.cc
+++ b/ash/launcher/overflow_bubble.cc
@@ -207,7 +207,7 @@ gfx::Rect OverflowBubbleView::GetBubbleBounds() {
kLauncherPreferredSize / 2;
const gfx::Size content_size = GetPreferredSize();
- border->SetArrowOffset(arrow_offset, content_size);
+ border->set_arrow_offset(arrow_offset);
const gfx::Rect anchor_rect = GetAnchorRect();
gfx::Rect bubble_rect = GetBubbleFrameView()->GetUpdatedWindowBounds(
@@ -226,8 +226,7 @@ gfx::Rect OverflowBubbleView::GetBubbleBounds() {
offset = monitor_rect.right() - bubble_rect.right();
bubble_rect.Offset(offset, 0);
- border->SetArrowOffset(anchor_rect.CenterPoint().x() - bubble_rect.x(),
- content_size);
+ border->set_arrow_offset(anchor_rect.CenterPoint().x() - bubble_rect.x());
} else {
if (bubble_rect.y() < monitor_rect.y())
offset = monitor_rect.y() - bubble_rect.y();
@@ -235,8 +234,7 @@ gfx::Rect OverflowBubbleView::GetBubbleBounds() {
offset = monitor_rect.bottom() - bubble_rect.bottom();
bubble_rect.Offset(0, offset);
- border->SetArrowOffset(anchor_rect.CenterPoint().y() - bubble_rect.y(),
- content_size);
+ border->set_arrow_offset(anchor_rect.CenterPoint().y() - bubble_rect.y());
}
GetBubbleFrameView()->SchedulePaint();
diff --git a/ash/wm/app_list_controller.cc b/ash/wm/app_list_controller.cc
index 262f91f..f6f9cdd 100644
--- a/ash/wm/app_list_controller.cc
+++ b/ash/wm/app_list_controller.cc
@@ -49,14 +49,14 @@ views::BubbleBorder::ArrowLocation GetBubbleArrowLocation() {
ShelfAlignment shelf_alignment = Shell::GetInstance()->GetShelfAlignment();
switch (shelf_alignment) {
case ash::SHELF_ALIGNMENT_BOTTOM:
- return views::BubbleBorder::BOTTOM_RIGHT;
+ return views::BubbleBorder::BOTTOM_CENTER;
case ash::SHELF_ALIGNMENT_LEFT:
- return views::BubbleBorder::LEFT_BOTTOM;
+ return views::BubbleBorder::LEFT_CENTER;
case ash::SHELF_ALIGNMENT_RIGHT:
- return views::BubbleBorder::RIGHT_BOTTOM;
+ return views::BubbleBorder::RIGHT_CENTER;
default:
NOTREACHED() << "Unknown shelf alignment " << shelf_alignment;
- return views::BubbleBorder::BOTTOM_RIGHT;
+ return views::BubbleBorder::BOTTOM_CENTER;
}
}
diff --git a/ash/wm/maximize_bubble_controller.cc b/ash/wm/maximize_bubble_controller.cc
index 1395d70..b26d903 100644
--- a/ash/wm/maximize_bubble_controller.cc
+++ b/ash/wm/maximize_bubble_controller.cc
@@ -86,7 +86,7 @@ MaximizeBubbleBorder::MaximizeBubbleBorder(views::View* content_view,
: views::BubbleBorder(views::BubbleBorder::TOP_RIGHT,
views::BubbleBorder::NO_SHADOW),
anchor_size_(anchor->size()),
- anchor_screen_origin_(0,0),
+ anchor_screen_origin_(0, 0),
content_view_(content_view) {
views::View::ConvertPointToScreen(anchor, &anchor_screen_origin_);
set_alignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
@@ -399,7 +399,7 @@ MaximizeBubbleController::Bubble::Bubble(
set_parent_window(parent);
set_notify_enter_exit_on_child(true);
- set_try_mirroring_arrow(false);
+ set_adjust_if_offscreen(false);
SetPaintToLayer(true);
SetFillsBoundsOpaquely(false);
set_color(kBubbleBackgroundColor);
diff --git a/ui/app_list/app_list.gyp b/ui/app_list/app_list.gyp
index 75c78e7..d364eff 100644
--- a/ui/app_list/app_list.gyp
+++ b/ui/app_list/app_list.gyp
@@ -22,8 +22,8 @@
'APP_LIST_IMPLEMENTATION',
],
'sources': [
- 'app_list_bubble_border.cc',
- 'app_list_bubble_border.h',
+ 'app_list_background.cc',
+ 'app_list_background.h',
'app_list_constants.cc',
'app_list_constants.h',
'app_list_export.h',
diff --git a/ui/app_list/app_list_bubble_border.cc b/ui/app_list/app_list_background.cc
index bfd134c..eef7f04 100644
--- a/ui/app_list/app_list_bubble_border.cc
+++ b/ui/app_list/app_list_background.cc
@@ -2,15 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/app_list/app_list_bubble_border.h"
+#include "ui/app_list/app_list_background.h"
-#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkPaint.h"
-#include "third_party/skia/include/effects/SkGradientShader.h"
+#include "third_party/skia/include/core/SkPath.h"
#include "ui/app_list/app_list_constants.h"
#include "ui/gfx/canvas.h"
-#include "ui/gfx/path.h"
+#include "ui/gfx/rect.h"
#include "ui/gfx/skia_util.h"
+#include "ui/views/view.h"
namespace {
@@ -24,25 +24,33 @@ const int kTopSeparatorSize = 1;
namespace app_list {
-AppListBubbleBorder::AppListBubbleBorder(views::View* app_list_view,
- views::View* search_box_view)
- : views::BubbleBorder2(views::BubbleBorder::BOTTOM_RIGHT),
- app_list_view_(app_list_view),
+AppListBackground::AppListBackground(int corner_radius,
+ views::View* search_box_view)
+ : corner_radius_(corner_radius),
search_box_view_(search_box_view) {
}
-AppListBubbleBorder::~AppListBubbleBorder() {
+AppListBackground::~AppListBackground() {
}
-void AppListBubbleBorder::PaintBackground(gfx::Canvas* canvas,
- const gfx::Rect& bounds) const {
+void AppListBackground::Paint(gfx::Canvas* canvas,
+ views::View* view) const {
+ gfx::Rect bounds = view->GetContentsBounds();
+
+ canvas->Save();
+ SkPath path;
+ // Contents corner radius is 1px smaller than border corner radius.
+ SkScalar radius = SkIntToScalar(corner_radius_ - 1);
+ path.addRoundRect(gfx::RectToSkRect(bounds), radius, radius);
+ canvas->ClipPath(path);
+
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
// TODO(benwells): Get these details painting on Windows.
#if !defined(OS_WIN)
const gfx::Rect search_box_view_bounds =
- app_list_view_->ConvertRectToWidget(search_box_view_->bounds());
+ search_box_view_->ConvertRectToWidget(search_box_view_->GetLocalBounds());
gfx::Rect search_box_rect(bounds.x(),
bounds.y(),
bounds.width(),
@@ -66,6 +74,7 @@ void AppListBubbleBorder::PaintBackground(gfx::Canvas* canvas,
paint.setColor(kContentsBackgroundColor);
canvas->DrawRect(contents_rect, paint);
+ canvas->Restore();
}
} // namespace app_list
diff --git a/ui/app_list/app_list_background.h b/ui/app_list/app_list_background.h
new file mode 100644
index 0000000..66be878
--- /dev/null
+++ b/ui/app_list/app_list_background.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 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 UI_APP_LIST_APP_LIST_BACKGROUND_H_
+#define UI_APP_LIST_APP_LIST_BACKGROUND_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/views/background.h"
+
+namespace views {
+class View;
+}
+
+namespace app_list {
+
+// A class to paint bubble background.
+class AppListBackground : public views::Background {
+ public:
+ AppListBackground(int corner_radius,
+ views::View* search_box_view);
+ virtual ~AppListBackground();
+
+ private:
+ // views::Background overrides:
+ virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE;
+
+ const int corner_radius_;
+ const views::View* search_box_view_; // Owned by views hierarchy.
+
+ DISALLOW_COPY_AND_ASSIGN(AppListBackground);
+};
+
+} // namespace app_list
+
+#endif // UI_APP_LIST_APP_LIST_BACKGROUND_H_
diff --git a/ui/app_list/app_list_bubble_border.h b/ui/app_list/app_list_bubble_border.h
deleted file mode 100644
index 901afd9..0000000
--- a/ui/app_list/app_list_bubble_border.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 2012 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 UI_APP_LIST_APP_LIST_BUBBLE_BORDER_H_
-#define UI_APP_LIST_APP_LIST_BUBBLE_BORDER_H_
-
-#include "base/basictypes.h"
-#include "ui/gfx/shadow_value.h"
-#include "ui/views/bubble/bubble_border_2.h"
-
-namespace app_list {
-
-// A class to paint bubble border and background.
-class AppListBubbleBorder : public views::BubbleBorder2 {
- public:
- AppListBubbleBorder(views::View* app_list_view,
- views::View* search_box_view);
- virtual ~AppListBubbleBorder();
-
- private:
- // views::BubbleBorder2 overrides:
- virtual void PaintBackground(gfx::Canvas* canvas,
- const gfx::Rect& bounds) const OVERRIDE;
-
- // AppListView hosted inside this bubble.
- const views::View* app_list_view_; // Owned by views hierarchy.
-
- // Children view of AppListView that needs to paint background.
- const views::View* search_box_view_; // Owned by views hierarchy.
-
- DISALLOW_COPY_AND_ASSIGN(AppListBubbleBorder);
-};
-
-} // namespace app_list
-
-#endif // UI_APP_LIST_APP_LIST_BUBBLE_BORDER_H_
diff --git a/ui/app_list/app_list_view.cc b/ui/app_list/app_list_view.cc
index 0cbd42b..8098b7d 100644
--- a/ui/app_list/app_list_view.cc
+++ b/ui/app_list/app_list_view.cc
@@ -5,7 +5,7 @@
#include "ui/app_list/app_list_view.h"
#include "base/string_util.h"
-#include "ui/app_list/app_list_bubble_border.h"
+#include "ui/app_list/app_list_background.h"
#include "ui/app_list/app_list_constants.h"
#include "ui/app_list/app_list_item_view.h"
#include "ui/app_list/app_list_model.h"
@@ -16,6 +16,8 @@
#include "ui/app_list/search_box_view.h"
#include "ui/base/events/event.h"
#include "ui/gfx/insets.h"
+#include "ui/gfx/path.h"
+#include "ui/gfx/skia_util.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/layout/box_layout.h"
@@ -38,7 +40,6 @@ const int kArrowOffset = 10;
AppListView::AppListView(AppListViewDelegate* delegate)
: delegate_(delegate),
- bubble_border_(NULL),
search_box_view_(NULL),
contents_view_(NULL) {
}
@@ -76,22 +77,23 @@ void AppListView::InitAsBubble(
set_anchor_view(anchor);
set_anchor_point(anchor_point);
+ set_color(kContentsBackgroundColor);
set_margins(gfx::Insets());
set_move_with_anchor(true);
set_parent_window(parent);
set_close_on_deactivate(false);
- set_anchor_insets(gfx::Insets(kArrowOffset, kArrowOffset, kArrowOffset,
- kArrowOffset));
+ // Shift anchor rect up 1px because app menu icon center is 1px above anchor
+ // rect center when shelf is on left/right.
+ set_anchor_insets(gfx::Insets(kArrowOffset - 1, kArrowOffset,
+ kArrowOffset + 1, kArrowOffset));
+ set_shadow(views::BubbleBorder::BIG_SHADOW);
views::BubbleDelegateView::CreateBubble(this);
-
- // Overrides border with AppListBubbleBorder.
- bubble_border_ = new AppListBubbleBorder(this, search_box_view_);
- GetBubbleFrameView()->SetBubbleBorder(bubble_border_);
SetBubbleArrowLocation(arrow_location);
#if !defined(OS_WIN)
- // Resets default background since AppListBubbleBorder paints background.
- GetBubbleFrameView()->set_background(NULL);
+ GetBubbleFrameView()->set_background(new AppListBackground(
+ GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
+ search_box_view_));
contents_view_->SetPaintToLayer(true);
contents_view_->SetFillsBoundsOpaquely(false);
@@ -103,9 +105,9 @@ void AppListView::InitAsBubble(
void AppListView::SetBubbleArrowLocation(
views::BubbleBorder::ArrowLocation arrow_location) {
- DCHECK(bubble_border_);
- bubble_border_->set_arrow_location(arrow_location);
+ GetBubbleFrameView()->bubble_border()->set_arrow_location(arrow_location);
SizeToContents(); // Recalcuates with new border.
+ GetBubbleFrameView()->SchedulePaint();
}
void AppListView::Close() {
@@ -149,7 +151,8 @@ bool AppListView::HasHitTestMask() const {
void AppListView::GetHitTestMask(gfx::Path* mask) const {
DCHECK(mask);
- bubble_border_->GetMask(GetBubbleFrameView()->bounds(), mask);
+ mask->addRect(gfx::RectToSkRect(
+ GetBubbleFrameView()->GetContentsBounds()));
}
bool AppListView::OnKeyPressed(const ui::KeyEvent& event) {
@@ -173,28 +176,6 @@ void AppListView::ButtonPressed(views::Button* sender, const ui::Event& event) {
Close();
}
-gfx::Rect AppListView::GetBubbleBounds() {
- // This happens before replacing the default border.
- if (!bubble_border_)
- return views::BubbleDelegateView::GetBubbleBounds();
-
- const gfx::Rect anchor_rect = GetAnchorRect();
- gfx::Rect bubble_rect = GetBubbleFrameView()->GetUpdatedWindowBounds(
- anchor_rect,
- GetPreferredSize(),
- false /* try_mirroring_arrow */);
-
- const gfx::Point old_offset = bubble_border_->offset();
- bubble_rect = bubble_border_->ComputeOffsetAndUpdateBubbleRect(bubble_rect,
- anchor_rect);
-
- // Repaints border if arrow offset is changed.
- if (bubble_border_->offset() != old_offset)
- GetBubbleFrameView()->SchedulePaint();
-
- return bubble_rect;
-}
-
void AppListView::QueryChanged(SearchBoxView* sender) {
string16 query;
TrimWhitespace(model_->search_box()->text(), TRIM_ALL, &query);
diff --git a/ui/app_list/app_list_view.h b/ui/app_list/app_list_view.h
index a4d5c67..8ffecb9 100644
--- a/ui/app_list/app_list_view.h
+++ b/ui/app_list/app_list_view.h
@@ -14,7 +14,6 @@
namespace app_list {
-class AppListBubbleBorder;
class AppListModel;
class AppListViewDelegate;
class ContentsView;
@@ -63,9 +62,6 @@ class APP_LIST_EXPORT AppListView : public views::BubbleDelegateView,
virtual void ButtonPressed(views::Button* sender,
const ui::Event& event) OVERRIDE;
- // Overridden from views::BubbleDelegate:
- virtual gfx::Rect GetBubbleBounds() OVERRIDE;
-
// Overridden from SearchBoxViewDelegate:
virtual void QueryChanged(SearchBoxView* sender) OVERRIDE;
@@ -79,7 +75,6 @@ class APP_LIST_EXPORT AppListView : public views::BubbleDelegateView,
scoped_ptr<AppListModel> model_;
scoped_ptr<AppListViewDelegate> delegate_;
- AppListBubbleBorder* bubble_border_; // Owned by views hierarchy.
SearchBoxView* search_box_view_; // Owned by views hierarchy.
ContentsView* contents_view_; // Owned by views hierarchy.
diff --git a/ui/resources/default_100_percent/common/window_bubble_shadow_spike_big_left.png b/ui/resources/default_100_percent/common/window_bubble_shadow_spike_big_left.png
index 982b20f..30dde27 100644
--- a/ui/resources/default_100_percent/common/window_bubble_shadow_spike_big_left.png
+++ b/ui/resources/default_100_percent/common/window_bubble_shadow_spike_big_left.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/window_bubble_shadow_spike_big_right.png b/ui/resources/default_100_percent/common/window_bubble_shadow_spike_big_right.png
index ff62e11..cf33fae 100644
--- a/ui/resources/default_100_percent/common/window_bubble_shadow_spike_big_right.png
+++ b/ui/resources/default_100_percent/common/window_bubble_shadow_spike_big_right.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/window_bubble_shadow_spike_small_left.png b/ui/resources/default_100_percent/common/window_bubble_shadow_spike_small_left.png
index 6e0c319..6fe7e0e 100644
--- a/ui/resources/default_100_percent/common/window_bubble_shadow_spike_small_left.png
+++ b/ui/resources/default_100_percent/common/window_bubble_shadow_spike_small_left.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/window_bubble_shadow_spike_small_right.png b/ui/resources/default_100_percent/common/window_bubble_shadow_spike_small_right.png
index 6d4f9dc..39f8e7b 100644
--- a/ui/resources/default_100_percent/common/window_bubble_shadow_spike_small_right.png
+++ b/ui/resources/default_100_percent/common/window_bubble_shadow_spike_small_right.png
Binary files differ
diff --git a/ui/resources/default_200_percent/common/window_bubble_shadow_spike_big_left.png b/ui/resources/default_200_percent/common/window_bubble_shadow_spike_big_left.png
index 65498b4..708f8e6 100644
--- a/ui/resources/default_200_percent/common/window_bubble_shadow_spike_big_left.png
+++ b/ui/resources/default_200_percent/common/window_bubble_shadow_spike_big_left.png
Binary files differ
diff --git a/ui/resources/default_200_percent/common/window_bubble_shadow_spike_big_right.png b/ui/resources/default_200_percent/common/window_bubble_shadow_spike_big_right.png
index 7f1e3cb..a781810 100644
--- a/ui/resources/default_200_percent/common/window_bubble_shadow_spike_big_right.png
+++ b/ui/resources/default_200_percent/common/window_bubble_shadow_spike_big_right.png
Binary files differ
diff --git a/ui/resources/default_200_percent/common/window_bubble_shadow_spike_small_left.png b/ui/resources/default_200_percent/common/window_bubble_shadow_spike_small_left.png
index daec7b0..08a4219 100644
--- a/ui/resources/default_200_percent/common/window_bubble_shadow_spike_small_left.png
+++ b/ui/resources/default_200_percent/common/window_bubble_shadow_spike_small_left.png
Binary files differ
diff --git a/ui/resources/default_200_percent/common/window_bubble_shadow_spike_small_right.png b/ui/resources/default_200_percent/common/window_bubble_shadow_spike_small_right.png
index d571618..65b7620 100644
--- a/ui/resources/default_200_percent/common/window_bubble_shadow_spike_small_right.png
+++ b/ui/resources/default_200_percent/common/window_bubble_shadow_spike_small_right.png
Binary files differ
diff --git a/ui/resources/ui_resources.grd b/ui/resources/ui_resources.grd
index c6c952f..102c9610 100644
--- a/ui/resources/ui_resources.grd
+++ b/ui/resources/ui_resources.grd
@@ -222,6 +222,30 @@
<structure type="chrome_scaled_image" name="IDR_WINDOW_TOP_CENTER" file="window_top_center.png" />
<structure type="chrome_scaled_image" name="IDR_WINDOW_TOP_LEFT_CORNER" file="window_top_left_corner.png" />
<structure type="chrome_scaled_image" name="IDR_WINDOW_TOP_RIGHT_CORNER" file="window_top_right_corner.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_BIG_BOTTOM" file="common/window_bubble_shadow_big_bottom.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_BIG_BOTTOM_LEFT" file="common/window_bubble_shadow_big_bottom_left.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_BIG_BOTTOM_RIGHT" file="common/window_bubble_shadow_big_bottom_right.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_BIG_LEFT" file="common/window_bubble_shadow_big_left.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_BIG_RIGHT" file="common/window_bubble_shadow_big_right.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_BIG_TOP" file="common/window_bubble_shadow_big_top.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_BIG_TOP_LEFT" file="common/window_bubble_shadow_big_top_left.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_BIG_TOP_RIGHT" file="common/window_bubble_shadow_big_top_right.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SPIKE_BIG_BOTTOM" file="common/window_bubble_shadow_spike_big_bottom.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SPIKE_BIG_LEFT" file="common/window_bubble_shadow_spike_big_left.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SPIKE_BIG_RIGHT" file="common/window_bubble_shadow_spike_big_right.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SPIKE_BIG_TOP" file="common/window_bubble_shadow_spike_big_top.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM" file="common/window_bubble_shadow_small_bottom.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM_LEFT" file="common/window_bubble_shadow_small_bottom_left.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM_RIGHT" file="common/window_bubble_shadow_small_bottom_right.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SMALL_LEFT" file="common/window_bubble_shadow_small_left.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SMALL_RIGHT" file="common/window_bubble_shadow_small_right.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP" file="common/window_bubble_shadow_small_top.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP_LEFT" file="common/window_bubble_shadow_small_top_left.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP_RIGHT" file="common/window_bubble_shadow_small_top_right.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SPIKE_SMALL_BOTTOM" file="common/window_bubble_shadow_spike_small_bottom.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SPIKE_SMALL_LEFT" file="common/window_bubble_shadow_spike_small_left.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SPIKE_SMALL_RIGHT" file="common/window_bubble_shadow_spike_small_right.png" />
+ <structure type="chrome_scaled_image" name="IDR_WINDOW_BUBBLE_SHADOW_SPIKE_SMALL_TOP" file="common/window_bubble_shadow_spike_small_top.png" />
</structures>
</release>
</grit>
diff --git a/ui/views/bubble/bubble_border.cc b/ui/views/bubble/bubble_border.cc
index 881f082..4a5547c 100644
--- a/ui/views/bubble/bubble_border.cc
+++ b/ui/views/bubble/bubble_border.cc
@@ -13,70 +13,150 @@
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/skia_util.h"
+namespace {
+
+// Stroke size in pixels of borders in image assets.
+const int kBorderStrokeSize = 1;
+
+// Border image resource ids.
+const int kShadowImages[] = {
+ IDR_BUBBLE_SHADOW_L,
+ IDR_BUBBLE_SHADOW_TL,
+ IDR_BUBBLE_SHADOW_T,
+ IDR_BUBBLE_SHADOW_TR,
+ IDR_BUBBLE_SHADOW_R,
+ IDR_BUBBLE_SHADOW_BR,
+ IDR_BUBBLE_SHADOW_B,
+ IDR_BUBBLE_SHADOW_BL,
+};
+
+const int kNoShadowImages[] = {
+ IDR_BUBBLE_L,
+ IDR_BUBBLE_TL,
+ IDR_BUBBLE_T,
+ IDR_BUBBLE_TR,
+ IDR_BUBBLE_R,
+ IDR_BUBBLE_BR,
+ IDR_BUBBLE_B,
+ IDR_BUBBLE_BL,
+ IDR_BUBBLE_L_ARROW,
+ IDR_BUBBLE_T_ARROW,
+ IDR_BUBBLE_R_ARROW,
+ IDR_BUBBLE_B_ARROW,
+};
+
+const int kBigShadowImages[] = {
+ IDR_WINDOW_BUBBLE_SHADOW_BIG_LEFT,
+ IDR_WINDOW_BUBBLE_SHADOW_BIG_TOP_LEFT,
+ IDR_WINDOW_BUBBLE_SHADOW_BIG_TOP,
+ IDR_WINDOW_BUBBLE_SHADOW_BIG_TOP_RIGHT,
+ IDR_WINDOW_BUBBLE_SHADOW_BIG_RIGHT,
+ IDR_WINDOW_BUBBLE_SHADOW_BIG_BOTTOM_RIGHT,
+ IDR_WINDOW_BUBBLE_SHADOW_BIG_BOTTOM,
+ IDR_WINDOW_BUBBLE_SHADOW_BIG_BOTTOM_LEFT,
+ IDR_WINDOW_BUBBLE_SHADOW_SPIKE_BIG_LEFT,
+ IDR_WINDOW_BUBBLE_SHADOW_SPIKE_BIG_TOP,
+ IDR_WINDOW_BUBBLE_SHADOW_SPIKE_BIG_RIGHT,
+ IDR_WINDOW_BUBBLE_SHADOW_SPIKE_BIG_BOTTOM,
+};
+
+const int kSmallShadowImages[] = {
+ IDR_WINDOW_BUBBLE_SHADOW_SMALL_LEFT,
+ IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP_LEFT,
+ IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP,
+ IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP_RIGHT,
+ IDR_WINDOW_BUBBLE_SHADOW_SMALL_RIGHT,
+ IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM_RIGHT,
+ IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM,
+ IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM_LEFT,
+ IDR_WINDOW_BUBBLE_SHADOW_SPIKE_SMALL_LEFT,
+ IDR_WINDOW_BUBBLE_SHADOW_SPIKE_SMALL_TOP,
+ IDR_WINDOW_BUBBLE_SHADOW_SPIKE_SMALL_RIGHT,
+ IDR_WINDOW_BUBBLE_SHADOW_SPIKE_SMALL_BOTTOM,
+};
+
+} // namespace
+
namespace views {
struct BubbleBorder::BorderImages {
- BorderImages()
- : left(NULL),
- top_left(NULL),
- top(NULL),
- top_right(NULL),
- right(NULL),
- bottom_right(NULL),
- bottom(NULL),
- bottom_left(NULL),
- left_arrow(NULL),
- top_arrow(NULL),
- right_arrow(NULL),
- bottom_arrow(NULL),
- border_thickness(0) {
+ BorderImages(const int image_ids[],
+ size_t image_ids_size,
+ int arrow_interior_height,
+ int border_thickness,
+ int corner_radius)
+ : arrow_interior_height(arrow_interior_height),
+ border_thickness(border_thickness),
+ corner_radius(corner_radius) {
+ // Only two possible sizes of image ids array.
+ DCHECK(image_ids_size == 12 || image_ids_size == 8);
+
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+
+ left = *rb.GetImageSkiaNamed(image_ids[0]);
+ top_left = *rb.GetImageSkiaNamed(image_ids[1]);
+ top = *rb.GetImageSkiaNamed(image_ids[2]);
+ top_right = *rb.GetImageSkiaNamed(image_ids[3]);
+ right = *rb.GetImageSkiaNamed(image_ids[4]);
+ bottom_right = *rb.GetImageSkiaNamed(image_ids[5]);
+ bottom = *rb.GetImageSkiaNamed(image_ids[6]);
+ bottom_left = *rb.GetImageSkiaNamed(image_ids[7]);
+
+ if (image_ids_size > 8) {
+ left_arrow = *rb.GetImageSkiaNamed(image_ids[8]);
+ top_arrow = *rb.GetImageSkiaNamed(image_ids[9]);
+ right_arrow = *rb.GetImageSkiaNamed(image_ids[10]);
+ bottom_arrow = *rb.GetImageSkiaNamed(image_ids[11]);
+ }
}
- gfx::ImageSkia* left;
- gfx::ImageSkia* top_left;
- gfx::ImageSkia* top;
- gfx::ImageSkia* top_right;
- gfx::ImageSkia* right;
- gfx::ImageSkia* bottom_right;
- gfx::ImageSkia* bottom;
- gfx::ImageSkia* bottom_left;
- gfx::ImageSkia* left_arrow;
- gfx::ImageSkia* top_arrow;
- gfx::ImageSkia* right_arrow;
- gfx::ImageSkia* bottom_arrow;
+ gfx::ImageSkia left;
+ gfx::ImageSkia top_left;
+ gfx::ImageSkia top;
+ gfx::ImageSkia top_right;
+ gfx::ImageSkia right;
+ gfx::ImageSkia bottom_right;
+ gfx::ImageSkia bottom;
+ gfx::ImageSkia bottom_left;
+
+ gfx::ImageSkia left_arrow;
+ gfx::ImageSkia top_arrow;
+ gfx::ImageSkia right_arrow;
+ gfx::ImageSkia bottom_arrow;
+
+ int arrow_interior_height; // The height inside the arrow image, in pixels.
int border_thickness;
+ int corner_radius;
};
// static
-struct BubbleBorder::BorderImages* BubbleBorder::normal_images_ = NULL;
-struct BubbleBorder::BorderImages* BubbleBorder::shadow_images_ = NULL;
-
-// The height inside the arrow image, in pixels.
-static const int kArrowInteriorHeight = 7;
+struct BubbleBorder::BorderImages*
+ BubbleBorder::border_images_[SHADOW_COUNT] = { NULL };
BubbleBorder::BubbleBorder(ArrowLocation arrow_location, Shadow shadow)
: override_arrow_offset_(0),
arrow_location_(arrow_location),
alignment_(ALIGN_ARROW_TO_MID_ANCHOR),
background_color_(SK_ColorWHITE) {
+ DCHECK(shadow < SHADOW_COUNT);
images_ = GetBorderImages(shadow);
// Calculate horizontal and vertical insets for arrow by ensuring that
// the widest arrow and corner images will have enough room to avoid overlap
int offset_x =
- (std::max(images_->top_arrow->width(),
- images_->bottom_arrow->width()) / 2) +
- std::max(std::max(images_->top_left->width(),
- images_->top_right->width()),
- std::max(images_->bottom_left->width(),
- images_->bottom_right->width()));
+ (std::max(images_->top_arrow.width(),
+ images_->bottom_arrow.width()) / 2) +
+ std::max(std::max(images_->top_left.width(),
+ images_->top_right.width()),
+ std::max(images_->bottom_left.width(),
+ images_->bottom_right.width()));
int offset_y =
- (std::max(images_->left_arrow->height(),
- images_->right_arrow->height()) / 2) +
- std::max(std::max(images_->top_left->height(),
- images_->top_right->height()),
- std::max(images_->bottom_left->height(),
- images_->bottom_right->height()));
+ (std::max(images_->left_arrow.height(),
+ images_->right_arrow.height()) / 2) +
+ std::max(std::max(images_->top_left.height(),
+ images_->top_right.height()),
+ std::max(images_->bottom_left.height(),
+ images_->bottom_right.height()));
arrow_offset_ = std::max(offset_x, offset_y);
}
@@ -95,39 +175,46 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& position_relative_to,
border_size.set_height(std::max(border_size.height(), 2 * arrow_offset_));
// Screen position depends on the arrow location.
- // The arrow should overlap the target by some amount since there is space
- // for shadow between arrow tip and image bounds.
- const int kArrowOverlap = 3;
+ // The bubble should overlap the target by some amount since there is space
+ // for shadow between arrow tip/bubble border and image bounds.
int x = position_relative_to.x();
int y = position_relative_to.y();
int w = position_relative_to.width();
int h = position_relative_to.height();
- int arrow_offset = override_arrow_offset_ ? override_arrow_offset_ :
- arrow_offset_;
+
+ const int arrow_size = images_->arrow_interior_height + kBorderStrokeSize;
+ const int arrow_offset = GetArrowOffset(border_size);
// Calculate bubble x coordinate.
switch (arrow_location_) {
case TOP_LEFT:
case BOTTOM_LEFT:
x += alignment_ == ALIGN_ARROW_TO_MID_ANCHOR ? w / 2 - arrow_offset :
- -kArrowOverlap;
+ -(images_->left.width() - kBorderStrokeSize);
break;
case TOP_RIGHT:
case BOTTOM_RIGHT:
x += alignment_ == ALIGN_ARROW_TO_MID_ANCHOR ?
w / 2 + arrow_offset - border_size.width() + 1 :
- w - border_size.width() + kArrowOverlap;
+ w - border_size.width() + images_->right.width() - kBorderStrokeSize;
break;
case LEFT_TOP:
+ case LEFT_CENTER:
case LEFT_BOTTOM:
- x += w - kArrowOverlap;
+ x += w - (images_->left_arrow.width() - arrow_size);
break;
case RIGHT_TOP:
+ case RIGHT_CENTER:
case RIGHT_BOTTOM:
- x += kArrowOverlap - border_size.width();
+ x += images_->right_arrow.width() - arrow_size - border_size.width();
+ break;
+
+ case TOP_CENTER:
+ case BOTTOM_CENTER:
+ x += w / 2 - arrow_offset;
break;
case NONE:
@@ -139,26 +226,35 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& position_relative_to,
// Calculate bubble y coordinate.
switch (arrow_location_) {
case TOP_LEFT:
+ case TOP_CENTER:
case TOP_RIGHT:
- y += h - kArrowOverlap;
+ y += h - (images_->top_arrow.height() - arrow_size);
break;
case BOTTOM_LEFT:
+ case BOTTOM_CENTER:
case BOTTOM_RIGHT:
- y += kArrowOverlap - border_size.height();
+ y += images_->bottom_arrow.height() - arrow_size -
+ border_size.height();
break;
case LEFT_TOP:
case RIGHT_TOP:
y += alignment_ == ALIGN_ARROW_TO_MID_ANCHOR ? h / 2 - arrow_offset :
- -kArrowOverlap;
+ -(images_->top.height() - kBorderStrokeSize);
break;
case LEFT_BOTTOM:
case RIGHT_BOTTOM:
y += alignment_ == ALIGN_ARROW_TO_MID_ANCHOR ?
h / 2 + arrow_offset - border_size.height() + 1 :
- h - border_size.height() + kArrowOverlap;
+ h - border_size.height() + images_->bottom.height() -
+ kBorderStrokeSize;
+ break;
+
+ case LEFT_CENTER:
+ case RIGHT_CENTER:
+ y += h / 2 - arrow_offset;
break;
case NONE:
@@ -179,29 +275,33 @@ void BubbleBorder::GetInsets(gfx::Insets* insets) const {
void BubbleBorder::GetInsetsForArrowLocation(gfx::Insets* insets,
ArrowLocation arrow_loc) const {
- int top = images_->top->height();
- int bottom = images_->bottom->height();
- int left = images_->left->width();
- int right = images_->right->width();
+ int top = images_->top.height();
+ int bottom = images_->bottom.height();
+ int left = images_->left.width();
+ int right = images_->right.width();
switch (arrow_loc) {
case TOP_LEFT:
+ case TOP_CENTER:
case TOP_RIGHT:
- top = std::max(top, images_->top_arrow->height());
+ top = std::max(top, images_->top_arrow.height());
break;
case BOTTOM_LEFT:
+ case BOTTOM_CENTER:
case BOTTOM_RIGHT:
- bottom = std::max(bottom, images_->bottom_arrow->height());
+ bottom = std::max(bottom, images_->bottom_arrow.height());
break;
case LEFT_TOP:
+ case LEFT_CENTER:
case LEFT_BOTTOM:
- left = std::max(left, images_->left_arrow->width());
+ left = std::max(left, images_->left_arrow.width());
break;
case RIGHT_TOP:
+ case RIGHT_CENTER:
case RIGHT_BOTTOM:
- right = std::max(right, images_->right_arrow->width());
+ right = std::max(right, images_->right_arrow.width());
break;
case NONE:
@@ -216,73 +316,83 @@ int BubbleBorder::GetBorderThickness() const {
return images_->border_thickness;
}
-int BubbleBorder::SetArrowOffset(int offset, const gfx::Size& contents_size) {
- gfx::Size border_size(contents_size);
- gfx::Insets insets;
- GetInsets(&insets);
- border_size.Enlarge(insets.left() + insets.right(),
- insets.top() + insets.bottom());
- offset = std::max(arrow_offset_,
- std::min(offset, (is_arrow_on_horizontal(arrow_location_) ?
+int BubbleBorder::GetBorderCornerRadius() const {
+ return images_->corner_radius;
+}
+
+int BubbleBorder::GetArrowOffset(const gfx::Size& border_size) const {
+ int arrow_offset = arrow_offset_;
+ if (override_arrow_offset_) {
+ arrow_offset = override_arrow_offset_;
+ } else if (is_arrow_at_center(arrow_location_)) {
+ if (is_arrow_on_horizontal(arrow_location_))
+ arrow_offset = border_size.width() / 2;
+ else
+ arrow_offset = border_size.height() / 2;
+ }
+
+ // |arrow_offset_| contains the minimum offset required to draw border images
+ // correctly. It's defined in terms of number of pixels from the beginning of
+ // the edge. The maximum arrow offset is the edge size - |arrow_offset_|. The
+ // following statement clamps the calculated arrow offset within that range.
+ return std::max(arrow_offset_,
+ std::min(arrow_offset, (is_arrow_on_horizontal(arrow_location_) ?
border_size.width() : border_size.height()) - arrow_offset_));
- override_arrow_offset_ = offset;
- return override_arrow_offset_;
}
// static
BubbleBorder::BorderImages* BubbleBorder::GetBorderImages(Shadow shadow) {
- if (shadow == SHADOW && shadow_images_ == NULL) {
- ResourceBundle& rb = ResourceBundle::GetSharedInstance();
- shadow_images_ = new BorderImages();
- shadow_images_->left = rb.GetImageSkiaNamed(IDR_BUBBLE_SHADOW_L);
- shadow_images_->top_left = rb.GetImageSkiaNamed(IDR_BUBBLE_SHADOW_TL);
- shadow_images_->top = rb.GetImageSkiaNamed(IDR_BUBBLE_SHADOW_T);
- shadow_images_->top_right = rb.GetImageSkiaNamed(IDR_BUBBLE_SHADOW_TR);
- shadow_images_->right = rb.GetImageSkiaNamed(IDR_BUBBLE_SHADOW_R);
- shadow_images_->bottom_right = rb.GetImageSkiaNamed(IDR_BUBBLE_SHADOW_BR);
- shadow_images_->bottom = rb.GetImageSkiaNamed(IDR_BUBBLE_SHADOW_B);
- shadow_images_->bottom_left = rb.GetImageSkiaNamed(IDR_BUBBLE_SHADOW_BL);
- shadow_images_->left_arrow = new gfx::ImageSkia();
- shadow_images_->top_arrow = new gfx::ImageSkia();
- shadow_images_->right_arrow = new gfx::ImageSkia();
- shadow_images_->bottom_arrow = new gfx::ImageSkia();
- shadow_images_->border_thickness = 10;
- } else if (shadow == NO_SHADOW && normal_images_ == NULL) {
- ResourceBundle& rb = ResourceBundle::GetSharedInstance();
- normal_images_ = new BorderImages();
- normal_images_->left = rb.GetImageSkiaNamed(IDR_BUBBLE_L);
- normal_images_->top_left = rb.GetImageSkiaNamed(IDR_BUBBLE_TL);
- normal_images_->top = rb.GetImageSkiaNamed(IDR_BUBBLE_T);
- normal_images_->top_right = rb.GetImageSkiaNamed(IDR_BUBBLE_TR);
- normal_images_->right = rb.GetImageSkiaNamed(IDR_BUBBLE_R);
- normal_images_->bottom_right = rb.GetImageSkiaNamed(IDR_BUBBLE_BR);
- normal_images_->bottom = rb.GetImageSkiaNamed(IDR_BUBBLE_B);
- normal_images_->bottom_left = rb.GetImageSkiaNamed(IDR_BUBBLE_BL);
- normal_images_->left_arrow = rb.GetImageSkiaNamed(IDR_BUBBLE_L_ARROW);
- normal_images_->top_arrow = rb.GetImageSkiaNamed(IDR_BUBBLE_T_ARROW);
- normal_images_->right_arrow = rb.GetImageSkiaNamed(IDR_BUBBLE_R_ARROW);
- normal_images_->bottom_arrow = rb.GetImageSkiaNamed(IDR_BUBBLE_B_ARROW);
- normal_images_->border_thickness = 0;
+ CHECK_LT(shadow, SHADOW_COUNT);
+
+ struct BorderImages*& images = border_images_[shadow];
+ if (images)
+ return images;
+
+ switch (shadow) {
+ case SHADOW:
+ images = new BorderImages(kShadowImages,
+ arraysize(kShadowImages),
+ 0, 10, 4);
+ break;
+ case NO_SHADOW:
+ images = new BorderImages(kNoShadowImages,
+ arraysize(kNoShadowImages),
+ 7, 0, 4);
+ break;
+ case BIG_SHADOW:
+ images = new BorderImages(kBigShadowImages,
+ arraysize(kBigShadowImages),
+ 9, 0, 3);
+ break;
+ case SMALL_SHADOW:
+ images = new BorderImages(kSmallShadowImages,
+ arraysize(kSmallShadowImages),
+ 9, 0, 3);
+ break;
+ case SHADOW_COUNT:
+ NOTREACHED();
+ break;
}
- return shadow == SHADOW ? shadow_images_ : normal_images_;
+
+ return images;
}
BubbleBorder::~BubbleBorder() {}
void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) const {
// Convenience shorthand variables.
- const int tl_width = images_->top_left->width();
- const int tl_height = images_->top_left->height();
- const int t_height = images_->top->height();
- const int tr_width = images_->top_right->width();
- const int tr_height = images_->top_right->height();
- const int l_width = images_->left->width();
- const int r_width = images_->right->width();
- const int br_width = images_->bottom_right->width();
- const int br_height = images_->bottom_right->height();
- const int b_height = images_->bottom->height();
- const int bl_width = images_->bottom_left->width();
- const int bl_height = images_->bottom_left->height();
+ const int tl_width = images_->top_left.width();
+ const int tl_height = images_->top_left.height();
+ const int t_height = images_->top.height();
+ const int tr_width = images_->top_right.width();
+ const int tr_height = images_->top_right.height();
+ const int l_width = images_->left.width();
+ const int r_width = images_->right.width();
+ const int br_width = images_->bottom_right.width();
+ const int br_height = images_->bottom_right.height();
+ const int b_height = images_->bottom.height();
+ const int bl_width = images_->bottom_left.width();
+ const int bl_height = images_->bottom_left.height();
gfx::Insets insets;
GetInsets(&insets);
@@ -293,29 +403,31 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) const {
const int height = bottom - top;
const int width = right - left;
- // |arrow_offset| is offset of arrow from the begining of the edge.
- int arrow_offset = arrow_offset_;
- if (override_arrow_offset_)
- arrow_offset = override_arrow_offset_;
- else if (is_arrow_on_horizontal(arrow_location_) &&
- !is_arrow_on_left(arrow_location_)) {
- arrow_offset = view.width() - arrow_offset - 1;
- } else if (!is_arrow_on_horizontal(arrow_location_) &&
- !is_arrow_on_top(arrow_location_)) {
- arrow_offset = view.height() - arrow_offset - 1;
+ // |arrow_offset| is offset of arrow from the beginning of the edge.
+ int arrow_offset = GetArrowOffset(view.size());
+ if (!is_arrow_at_center(arrow_location_)) {
+ if (is_arrow_on_horizontal(arrow_location_) &&
+ !is_arrow_on_left(arrow_location_)) {
+ arrow_offset = view.width() - arrow_offset - 1;
+ } else if (!is_arrow_on_horizontal(arrow_location_) &&
+ !is_arrow_on_top(arrow_location_)) {
+ arrow_offset = view.height() - arrow_offset - 1;
+ }
}
// Left edge.
- if (arrow_location_ == LEFT_TOP || arrow_location_ == LEFT_BOTTOM) {
+ if (arrow_location_ == LEFT_TOP ||
+ arrow_location_ == LEFT_CENTER ||
+ arrow_location_ == LEFT_BOTTOM) {
int start_y = top + tl_height;
int before_arrow =
- arrow_offset - start_y - images_->left_arrow->height() / 2;
+ arrow_offset - start_y - images_->left_arrow.height() / 2;
int after_arrow = height - tl_height - bl_height -
- images_->left_arrow->height() - before_arrow;
+ images_->left_arrow.height() - before_arrow;
// Shift tip coordinates half pixel so that skia draws the tip correctly.
DrawArrowInterior(canvas,
- images_->left_arrow->width() - kArrowInteriorHeight - 0.5f,
- start_y + before_arrow + images_->left_arrow->height() / 2 - 0.5f);
+ images_->left_arrow.width() - images_->arrow_interior_height - 0.5f,
+ start_y + before_arrow + images_->left_arrow.height() / 2 - 0.5f);
DrawEdgeWithArrow(canvas,
false,
images_->left,
@@ -324,24 +436,26 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) const {
start_y,
before_arrow,
after_arrow,
- images_->left->width() - images_->left_arrow->width());
+ images_->left.width() - images_->left_arrow.width());
} else {
- canvas->TileImageInt(*images_->left, left, top + tl_height, l_width,
+ canvas->TileImageInt(images_->left, left, top + tl_height, l_width,
height - tl_height - bl_height);
}
// Top left corner.
- canvas->DrawImageInt(*images_->top_left, left, top);
+ canvas->DrawImageInt(images_->top_left, left, top);
// Top edge.
- if (arrow_location_ == TOP_LEFT || arrow_location_ == TOP_RIGHT) {
+ if (arrow_location_ == TOP_LEFT ||
+ arrow_location_ == TOP_CENTER ||
+ arrow_location_ == TOP_RIGHT) {
int start_x = left + tl_width;
- int before_arrow = arrow_offset - start_x - images_->top_arrow->width() / 2;
+ int before_arrow = arrow_offset - start_x - images_->top_arrow.width() / 2;
int after_arrow = width - tl_width - tr_width -
- images_->top_arrow->width() - before_arrow;
+ images_->top_arrow.width() - before_arrow;
DrawArrowInterior(canvas,
- start_x + before_arrow + images_->top_arrow->width() / 2,
- images_->top_arrow->height() - kArrowInteriorHeight);
+ start_x + before_arrow + images_->top_arrow.width() / 2,
+ images_->top_arrow.height() - images_->arrow_interior_height);
DrawEdgeWithArrow(canvas,
true,
images_->top,
@@ -350,26 +464,28 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) const {
top,
before_arrow,
after_arrow,
- images_->top->height() - images_->top_arrow->height());
+ images_->top.height() - images_->top_arrow.height());
} else {
- canvas->TileImageInt(*images_->top, left + tl_width, top,
+ canvas->TileImageInt(images_->top, left + tl_width, top,
width - tl_width - tr_width, t_height);
}
// Top right corner.
- canvas->DrawImageInt(*images_->top_right, right - tr_width, top);
+ canvas->DrawImageInt(images_->top_right, right - tr_width, top);
// Right edge.
- if (arrow_location_ == RIGHT_TOP || arrow_location_ == RIGHT_BOTTOM) {
+ if (arrow_location_ == RIGHT_TOP ||
+ arrow_location_ == RIGHT_CENTER ||
+ arrow_location_ == RIGHT_BOTTOM) {
int start_y = top + tr_height;
int before_arrow =
- arrow_offset - start_y - images_->right_arrow->height() / 2;
+ arrow_offset - start_y - images_->right_arrow.height() / 2;
int after_arrow = height - tl_height - bl_height -
- images_->right_arrow->height() - before_arrow;
+ images_->right_arrow.height() - before_arrow;
// Shift tip coordinates half pixel so that skia draws the tip correctly.
DrawArrowInterior(canvas,
- right - r_width + kArrowInteriorHeight - 0.5f,
- start_y + before_arrow + images_->right_arrow->height() / 2 - 0.5f);
+ right - r_width + images_->arrow_interior_height - 0.5f,
+ start_y + before_arrow + images_->right_arrow.height() / 2 - 0.5f);
DrawEdgeWithArrow(canvas,
false,
images_->right,
@@ -380,25 +496,27 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) const {
after_arrow,
0);
} else {
- canvas->TileImageInt(*images_->right, right - r_width, top + tr_height,
+ canvas->TileImageInt(images_->right, right - r_width, top + tr_height,
r_width, height - tr_height - br_height);
}
// Bottom right corner.
- canvas->DrawImageInt(*images_->bottom_right,
+ canvas->DrawImageInt(images_->bottom_right,
right - br_width,
bottom - br_height);
// Bottom edge.
- if (arrow_location_ == BOTTOM_LEFT || arrow_location_ == BOTTOM_RIGHT) {
+ if (arrow_location_ == BOTTOM_LEFT ||
+ arrow_location_ == BOTTOM_CENTER ||
+ arrow_location_ == BOTTOM_RIGHT) {
int start_x = left + bl_width;
int before_arrow =
- arrow_offset - start_x - images_->bottom_arrow->width() / 2;
+ arrow_offset - start_x - images_->bottom_arrow.width() / 2;
int after_arrow = width - bl_width - br_width -
- images_->bottom_arrow->width() - before_arrow;
+ images_->bottom_arrow.width() - before_arrow;
DrawArrowInterior(canvas,
- start_x + before_arrow + images_->bottom_arrow->width() / 2,
- bottom - b_height + kArrowInteriorHeight);
+ start_x + before_arrow + images_->bottom_arrow.width() / 2,
+ bottom - b_height + images_->arrow_interior_height);
DrawEdgeWithArrow(canvas,
true,
images_->bottom,
@@ -409,18 +527,18 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) const {
after_arrow,
0);
} else {
- canvas->TileImageInt(*images_->bottom, left + bl_width, bottom - b_height,
+ canvas->TileImageInt(images_->bottom, left + bl_width, bottom - b_height,
width - bl_width - br_width, b_height);
}
// Bottom left corner.
- canvas->DrawImageInt(*images_->bottom_left, left, bottom - bl_height);
+ canvas->DrawImageInt(images_->bottom_left, left, bottom - bl_height);
}
void BubbleBorder::DrawEdgeWithArrow(gfx::Canvas* canvas,
bool is_horizontal,
- gfx::ImageSkia* edge,
- gfx::ImageSkia* arrow,
+ const gfx::ImageSkia& edge,
+ const gfx::ImageSkia& arrow,
int start_x,
int start_y,
int before_arrow,
@@ -438,21 +556,21 @@ void BubbleBorder::DrawEdgeWithArrow(gfx::Canvas* canvas,
* before_arrow ─┘ └─ after_arrow
*/
if (before_arrow) {
- canvas->TileImageInt(*edge, start_x, start_y,
- is_horizontal ? before_arrow : edge->width(),
- is_horizontal ? edge->height() : before_arrow);
+ canvas->TileImageInt(edge, start_x, start_y,
+ is_horizontal ? before_arrow : edge.width(),
+ is_horizontal ? edge.height() : before_arrow);
}
- canvas->DrawImageInt(*arrow,
+ canvas->DrawImageInt(arrow,
start_x + (is_horizontal ? before_arrow : offset),
start_y + (is_horizontal ? offset : before_arrow));
if (after_arrow) {
- start_x += (is_horizontal ? before_arrow + arrow->width() : 0);
- start_y += (is_horizontal ? 0 : before_arrow + arrow->height());
- canvas->TileImageInt(*edge, start_x, start_y,
- is_horizontal ? after_arrow : edge->width(),
- is_horizontal ? edge->height() : after_arrow);
+ start_x += (is_horizontal ? before_arrow + arrow.width() : 0);
+ start_y += (is_horizontal ? 0 : before_arrow + arrow.height());
+ canvas->TileImageInt(edge, start_x, start_y,
+ is_horizontal ? after_arrow : edge.width(),
+ is_horizontal ? edge.height() : after_arrow);
}
}
@@ -463,7 +581,7 @@ void BubbleBorder::DrawArrowInterior(gfx::Canvas* canvas,
const bool positive_offset = is_horizontal ?
is_arrow_on_top(arrow_location_) : is_arrow_on_left(arrow_location_);
const int offset_to_next_vertex = positive_offset ?
- kArrowInteriorHeight : -kArrowInteriorHeight;
+ images_->arrow_interior_height : -images_->arrow_interior_height;
SkPath path;
path.incReserve(4);
@@ -506,7 +624,7 @@ void BubbleBackground::Paint(gfx::Canvas* canvas, views::View* view) const {
SkPath path;
gfx::Rect bounds(view->GetContentsBounds());
bounds.Inset(-border_->GetBorderThickness(), -border_->GetBorderThickness());
- SkScalar radius = SkIntToScalar(BubbleBorder::GetCornerRadius());
+ SkScalar radius = SkIntToScalar(border_->GetBorderCornerRadius());
path.addRoundRect(gfx::RectToSkRect(bounds), radius, radius);
canvas->DrawPath(path, paint);
}
diff --git a/ui/views/bubble/bubble_border.h b/ui/views/bubble/bubble_border.h
index 17d1de7..d883ca7 100644
--- a/ui/views/bubble/bubble_border.h
+++ b/ui/views/bubble/bubble_border.h
@@ -24,22 +24,37 @@ class VIEWS_EXPORT BubbleBorder : public Border {
// 0 bit specifies left or right.
// 1 bit specifies top or bottom.
// 2 bit specifies horizontal or vertical.
+ // 3 bit specifies whether the arrow at the center of its residing edge.
+ enum ArrowLocationMask {
+ RIGHT = 0x01,
+ BOTTOM = 0x02,
+ VERTICAL = 0x04,
+ CENTER = 0x08,
+ };
+
enum ArrowLocation {
- TOP_LEFT = 0,
- TOP_RIGHT = 1,
- BOTTOM_LEFT = 2,
- BOTTOM_RIGHT = 3,
- LEFT_TOP = 4,
- RIGHT_TOP = 5,
- LEFT_BOTTOM = 6,
- RIGHT_BOTTOM = 7,
- NONE = 8, // No arrow. Positioned under the supplied rect.
- FLOAT = 9 // No arrow. Centered over the supplied rect.
+ TOP_LEFT = 0,
+ TOP_RIGHT = RIGHT,
+ BOTTOM_LEFT = BOTTOM,
+ BOTTOM_RIGHT = BOTTOM | RIGHT,
+ LEFT_TOP = VERTICAL,
+ RIGHT_TOP = VERTICAL | RIGHT,
+ LEFT_BOTTOM = VERTICAL | BOTTOM,
+ RIGHT_BOTTOM = VERTICAL | BOTTOM | RIGHT,
+ TOP_CENTER = CENTER,
+ BOTTOM_CENTER = CENTER | BOTTOM,
+ LEFT_CENTER = CENTER | VERTICAL,
+ RIGHT_CENTER = CENTER | VERTICAL | RIGHT,
+ NONE = 16, // No arrow. Positioned under the supplied rect.
+ FLOAT = 17, // No arrow. Centered over the supplied rect.
};
enum Shadow {
SHADOW = 0,
- NO_SHADOW = 1
+ NO_SHADOW,
+ BIG_SHADOW,
+ SMALL_SHADOW,
+ SHADOW_COUNT,
};
// The position of the bubble in relation to the anchor.
@@ -53,6 +68,7 @@ class VIEWS_EXPORT BubbleBorder : public Border {
BubbleBorder(ArrowLocation arrow_location, Shadow shadow);
// Returns the radius of the corner of the border.
+ // TODO(xiyuan): Get rid of this since it's part of BorderImages now?
static int GetCornerRadius() {
// We can't safely calculate a border radius by comparing the sizes of the
// side and corner images, because either may have been extended in various
@@ -70,11 +86,13 @@ class VIEWS_EXPORT BubbleBorder : public Border {
BubbleAlignment alignment() const { return alignment_; }
static ArrowLocation horizontal_mirror(ArrowLocation loc) {
- return loc >= NONE ? loc : static_cast<ArrowLocation>(loc ^ 1);
+ return (loc == TOP_CENTER || loc == BOTTOM_CENTER || loc >= NONE) ?
+ loc : static_cast<ArrowLocation>(loc ^ RIGHT);
}
static ArrowLocation vertical_mirror(ArrowLocation loc) {
- return loc >= NONE ? loc : static_cast<ArrowLocation>(loc ^ 2);
+ return (loc == LEFT_CENTER || loc == RIGHT_CENTER || loc >= NONE) ?
+ loc : static_cast<ArrowLocation>(loc ^ BOTTOM);
}
static bool has_arrow(ArrowLocation loc) {
@@ -82,15 +100,21 @@ class VIEWS_EXPORT BubbleBorder : public Border {
}
static bool is_arrow_on_left(ArrowLocation loc) {
- return loc >= NONE ? false : !(loc & 1);
+ return (loc == TOP_CENTER || loc == BOTTOM_CENTER || loc >= NONE) ?
+ false : !(loc & RIGHT);
}
static bool is_arrow_on_top(ArrowLocation loc) {
- return loc >= NONE ? false : !(loc & 2);
+ return (loc == LEFT_CENTER || loc == RIGHT_CENTER || loc >= NONE) ?
+ false : !(loc & BOTTOM);
}
static bool is_arrow_on_horizontal(ArrowLocation loc) {
- return loc >= NONE ? false : !(loc & 4);
+ return loc >= NONE ? false : !(loc & VERTICAL);
+ }
+
+ static bool is_arrow_at_center(ArrowLocation loc) {
+ return has_arrow(loc) && !!(loc & CENTER);
}
// Sets the background color for the arrow body. This is irrelevant if you do
@@ -101,6 +125,11 @@ class VIEWS_EXPORT BubbleBorder : public Border {
void set_client_bounds(const gfx::Rect& bounds) { client_bounds_ = bounds; }
const gfx::Rect& client_bounds() const { return client_bounds_; }
+ // Sets a fixed offset for the arrow from the beginning of corresponding edge.
+ // The arrow will still point to the same location but the bubble will shift
+ // location to make that happen.
+ void set_arrow_offset(int offset) { override_arrow_offset_ = offset; }
+
// For borders with an arrow, gives the desired bounds (in screen coordinates)
// given the rect to point to and the size of the contained contents. This
// depends on the arrow location, so if you change that, you should call this
@@ -108,11 +137,11 @@ class VIEWS_EXPORT BubbleBorder : public Border {
virtual gfx::Rect GetBounds(const gfx::Rect& position_relative_to,
const gfx::Size& contents_size) const;
- // Sets a fixed offset for the arrow from the beginning of corresponding edge.
- // The arrow will still point to the same location but the bubble will shift
- // location to make that happen. Returns actuall arrow offset, in case of
- // overflow it differ from desired.
- int SetArrowOffset(int offset, const gfx::Size& contents_size);
+ // Returns the corner radius of the current image set.
+ int GetBorderCornerRadius() const;
+
+ // Gets the arrow offset to use.
+ int GetArrowOffset(const gfx::Size& border_size) const;
// Overridden from Border:
virtual void GetInsets(gfx::Insets* insets) const OVERRIDE;
@@ -141,8 +170,8 @@ class VIEWS_EXPORT BubbleBorder : public Border {
void DrawEdgeWithArrow(gfx::Canvas* canvas,
bool is_horizontal,
- gfx::ImageSkia* edge,
- gfx::ImageSkia* arrow,
+ const gfx::ImageSkia& edge,
+ const gfx::ImageSkia& arrow,
int start_x,
int start_y,
int before_arrow,
@@ -155,8 +184,7 @@ class VIEWS_EXPORT BubbleBorder : public Border {
struct BorderImages* images_;
// Image bundles.
- static struct BorderImages* normal_images_;
- static struct BorderImages* shadow_images_;
+ static struct BorderImages* border_images_[SHADOW_COUNT];
// Minimal offset of the arrow from the closet edge of bounding rect.
int arrow_offset_;
diff --git a/ui/views/bubble/bubble_border_2.cc b/ui/views/bubble/bubble_border_2.cc
deleted file mode 100644
index 0f909d1..0000000
--- a/ui/views/bubble/bubble_border_2.cc
+++ /dev/null
@@ -1,399 +0,0 @@
-// Copyright (c) 2012 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 "ui/views/bubble/bubble_border_2.h"
-
-#include <algorithm> // for std::max
-
-#include "base/logging.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/path.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/screen.h"
-#include "ui/gfx/skia_util.h"
-
-namespace {
-
-// Bubble border corner radius.
-const int kCornerRadius = 2;
-
-// Arrow width and height.
-const int kArrowHeight = 10;
-const int kArrowWidth = 20;
-
-const int kBorderSize = 1;
-const SkColor kBorderColor = SkColorSetARGB(0x26, 0, 0, 0);
-const SkColor kBackgroundColor = SK_ColorWHITE;
-
-const int kShadowOffsetX = 0;
-const int kShadowOffsetY = 5;
-const double kShadowBlur = 30;
-const SkColor kShadowColor = SkColorSetARGB(0x72, 0, 0, 0);
-
-// Builds a bubble shape for given |bounds|.
-void BuildShape(const gfx::Rect& bounds,
- views::BubbleBorder::ArrowLocation arrow_location,
- SkScalar arrow_offset,
- SkScalar padding,
- SkPath* path,
- int corner_radius_int,
- int arrow_height_int,
- int arrow_width_int) {
- const SkScalar corner_radius = SkIntToScalar(corner_radius_int);
-
- const SkScalar left = SkIntToScalar(bounds.x()) + padding;
- const SkScalar top = SkIntToScalar(bounds.y()) + padding;
- const SkScalar right = SkIntToScalar(bounds.right()) - padding;
- const SkScalar bottom = SkIntToScalar(bounds.bottom()) - padding;
-
- const SkScalar center_x = SkIntToScalar((bounds.x() + bounds.right()) / 2);
- const SkScalar center_y = SkIntToScalar((bounds.y() + bounds.bottom()) / 2);
-
- const SkScalar half_arrow_width =
- (SkIntToScalar(arrow_width_int) - padding) / 2;
- const SkScalar arrow_height = SkIntToScalar(arrow_height_int) - padding;
-
- path->reset();
- path->incReserve(12);
-
- switch (arrow_location) {
- case views::BubbleBorder::TOP_LEFT:
- case views::BubbleBorder::TOP_RIGHT:
- path->moveTo(center_x, bottom);
- path->arcTo(right, bottom, right, center_y, corner_radius);
- path->arcTo(right, top, center_x - half_arrow_width, top,
- corner_radius);
- path->lineTo(center_x + arrow_offset + half_arrow_width, top);
- path->lineTo(center_x + arrow_offset, top - arrow_height);
- path->lineTo(center_x + arrow_offset - half_arrow_width, top);
- path->arcTo(left, top, left, center_y, corner_radius);
- path->arcTo(left, bottom, center_x, bottom, corner_radius);
- break;
- case views::BubbleBorder::BOTTOM_LEFT:
- case views::BubbleBorder::BOTTOM_RIGHT:
- path->moveTo(center_x, top);
- path->arcTo(left, top, left, center_y, corner_radius);
- path->arcTo(left, bottom, center_x - half_arrow_width, bottom,
- corner_radius);
- path->lineTo(center_x + arrow_offset - half_arrow_width, bottom);
- path->lineTo(center_x + arrow_offset, bottom + arrow_height);
- path->lineTo(center_x + arrow_offset + half_arrow_width, bottom);
- path->arcTo(right, bottom, right, center_y, corner_radius);
- path->arcTo(right, top, center_x, top, corner_radius);
- break;
- case views::BubbleBorder::LEFT_TOP:
- case views::BubbleBorder::LEFT_BOTTOM:
- path->moveTo(right, center_y);
- path->arcTo(right, top, center_x, top, corner_radius);
- path->arcTo(left, top, left, center_y + arrow_offset - half_arrow_width,
- corner_radius);
- path->lineTo(left, center_y + arrow_offset - half_arrow_width);
- path->lineTo(left - arrow_height, center_y + arrow_offset);
- path->lineTo(left, center_y + arrow_offset + half_arrow_width);
- path->arcTo(left, bottom, center_x, bottom, corner_radius);
- path->arcTo(right, bottom, right, center_y, corner_radius);
- break;
- case views::BubbleBorder::RIGHT_TOP:
- case views::BubbleBorder::RIGHT_BOTTOM:
- path->moveTo(left, center_y);
- path->arcTo(left, bottom, center_x, bottom, corner_radius);
- path->arcTo(right, bottom,
- right, center_y + arrow_offset + half_arrow_width,
- corner_radius);
- path->lineTo(right, center_y + arrow_offset + half_arrow_width);
- path->lineTo(right + arrow_height, center_y + arrow_offset);
- path->lineTo(right, center_y + arrow_offset - half_arrow_width);
- path->arcTo(right, top, center_x, top, corner_radius);
- path->arcTo(left, top, left, center_y, corner_radius);
- break;
- default:
- // No arrows.
- path->addRoundRect(gfx::RectToSkRect(bounds),
- corner_radius,
- corner_radius);
- break;
- }
-
- path->close();
-}
-
-} // namespace
-
-namespace views {
-
-BubbleBorder2::BubbleBorder2(ArrowLocation arrow_location)
- : BubbleBorder(arrow_location, views::BubbleBorder::NO_SHADOW),
- corner_radius_(kCornerRadius),
- border_size_(kBorderSize),
- arrow_height_(kArrowHeight),
- arrow_width_(kArrowWidth),
- background_color_(kBackgroundColor),
- border_color_(kBorderColor) {
- SetShadow(gfx::ShadowValue(gfx::Point(kShadowOffsetX, kShadowOffsetY),
- kShadowBlur, kShadowColor));
-}
-
-BubbleBorder2::~BubbleBorder2() {}
-
-gfx::Rect BubbleBorder2::ComputeOffsetAndUpdateBubbleRect(
- gfx::Rect bubble_rect,
- const gfx::Rect& anchor_view_rect) {
- offset_ = gfx::Point();
-
- gfx::Rect monitor_rect = gfx::Screen::GetDisplayNearestPoint(
- anchor_view_rect.CenterPoint()).bounds();
- if (monitor_rect.IsEmpty() || monitor_rect.Contains(bubble_rect))
- return bubble_rect;
-
- gfx::Point offset;
-
- if (has_arrow(arrow_location())) {
- if (is_arrow_on_horizontal(arrow_location())) {
- if (bubble_rect.x() < monitor_rect.x())
- offset.set_x(monitor_rect.x() - bubble_rect.x());
- else if (bubble_rect.right() > monitor_rect.right())
- offset.set_x(monitor_rect.right() - bubble_rect.right());
- } else {
- if (bubble_rect.y() < monitor_rect.y())
- offset.set_y(monitor_rect.y() - bubble_rect.y());
- else if (bubble_rect.bottom() > monitor_rect.bottom())
- offset.set_y(monitor_rect.bottom() - bubble_rect.bottom());
- }
- }
-
- bubble_rect.Offset(offset);
- set_offset(offset);
-
- return bubble_rect;
-}
-
-void BubbleBorder2::GetMask(const gfx::Rect& bounds,
- gfx::Path* mask) const {
- gfx::Insets insets;
- GetInsets(&insets);
-
- gfx::Rect content_bounds(bounds);
- content_bounds.Inset(insets);
-
- BuildShape(content_bounds,
- arrow_location(),
- SkIntToScalar(GetArrowOffset()),
- SkIntToScalar(kBorderSize),
- mask,
- corner_radius_,
- arrow_height_,
- arrow_width_);
-}
-
-void BubbleBorder2::SetShadow(gfx::ShadowValue shadow) {
- shadows_.clear();
- shadows_.push_back(shadow);
-}
-
-int BubbleBorder2::GetBorderThickness() const {
- return border_size_;
-}
-
-void BubbleBorder2::PaintBackground(gfx::Canvas* canvas,
- const gfx::Rect& bounds) const {
- canvas->FillRect(bounds, background_color_);
-}
-
-int BubbleBorder2::GetArrowOffset() const {
- if (has_arrow(arrow_location())) {
- if (is_arrow_on_horizontal(arrow_location())) {
- // Picks x offset and moves bubble arrow in the opposite direction.
- // i.e. If bubble bounds is moved to right (positive offset), we need to
- // move arrow to left so that it points to the same position.
- return -offset_.x();
- } else {
- // Picks y offset and moves bubble arrow in the opposite direction.
- return -offset_.y();
- }
- }
-
- // Other style does not have an arrow, so return 0.
- return 0;
-}
-
-void BubbleBorder2::GetInsets(gfx::Insets* insets) const {
- // Negate to change from outer margin to inner padding.
- gfx::Insets shadow_padding(-gfx::ShadowValue::GetMargin(shadows_));
-
- if (arrow_location() == views::BubbleBorder::TOP_LEFT ||
- arrow_location() == views::BubbleBorder::TOP_RIGHT) {
- // Arrow at top.
- insets->Set(shadow_padding.top() + arrow_height_,
- shadow_padding.left(),
- shadow_padding.bottom(),
- shadow_padding.right());
- } else if (arrow_location() == views::BubbleBorder::BOTTOM_LEFT ||
- arrow_location() == views::BubbleBorder::BOTTOM_RIGHT) {
- // Arrow at bottom.
- insets->Set(shadow_padding.top(),
- shadow_padding.left(),
- shadow_padding.bottom() + arrow_height_,
- shadow_padding.right());
- } else if (arrow_location() == views::BubbleBorder::LEFT_TOP ||
- arrow_location() == views::BubbleBorder::LEFT_BOTTOM) {
- // Arrow on left.
- insets->Set(shadow_padding.top(),
- shadow_padding.left() + arrow_height_,
- shadow_padding.bottom(),
- shadow_padding.right());
- } else if (arrow_location() == views::BubbleBorder::RIGHT_TOP ||
- arrow_location() == views::BubbleBorder::RIGHT_BOTTOM) {
- // Arrow on right.
- insets->Set(shadow_padding.top(),
- shadow_padding.left(),
- shadow_padding.bottom(),
- shadow_padding.right() + arrow_height_);
- }
-}
-
-gfx::Rect BubbleBorder2::GetBounds(const gfx::Rect& position_relative_to,
- const gfx::Size& contents_size) const {
- gfx::Size border_size(contents_size);
- gfx::Insets insets;
- GetInsets(&insets);
- border_size.Enlarge(insets.width(), insets.height());
-
- // Negate to change from outer margin to inner padding.
- gfx::Insets shadow_padding(-gfx::ShadowValue::GetMargin(shadows_));
-
- // Anchor center that arrow aligns with.
- const int anchor_center_x =
- (position_relative_to.x() + position_relative_to.right()) / 2;
- const int anchor_center_y =
- (position_relative_to.y() + position_relative_to.bottom()) / 2;
-
- // Arrow position relative to top-left of bubble. |arrow_tip_x| is used for
- // arrow at the top or bottom and |arrow_tip_y| is used for arrow on left or
- // right. The 1px offset for |arrow_tip_y| is needed because the app list grid
- // icon start at a different position (1px earlier) compared with bottom
- // launcher bar.
- // TODO(xiyuan): Remove 1px offset when app list icon image asset is updated.
- int arrow_tip_x = insets.left() + contents_size.width() / 2 +
- GetArrowOffset();
- int arrow_tip_y = insets.top() + contents_size.height() / 2 +
- GetArrowOffset() + 1;
-
- if (arrow_location() == views::BubbleBorder::TOP_LEFT ||
- arrow_location() == views::BubbleBorder::TOP_RIGHT) {
- // Arrow at top.
- return gfx::Rect(
- gfx::Point(anchor_center_x - arrow_tip_x,
- position_relative_to.bottom() - shadow_padding.top()),
- border_size);
- } else if (arrow_location() == views::BubbleBorder::BOTTOM_LEFT ||
- arrow_location() == views::BubbleBorder::BOTTOM_RIGHT) {
- // Arrow at bottom.
- return gfx::Rect(
- gfx::Point(anchor_center_x - arrow_tip_x,
- position_relative_to.y() - border_size.height() +
- shadow_padding.bottom()),
- border_size);
- } else if (arrow_location() == views::BubbleBorder::LEFT_TOP ||
- arrow_location() == views::BubbleBorder::LEFT_BOTTOM) {
- // Arrow on left.
- return gfx::Rect(
- gfx::Point(position_relative_to.right() - shadow_padding.left(),
- anchor_center_y - arrow_tip_y),
- border_size);
- } else if (arrow_location() == views::BubbleBorder::RIGHT_TOP ||
- arrow_location() == views::BubbleBorder::RIGHT_BOTTOM) {
- // Arrow on right.
- return gfx::Rect(
- gfx::Point(position_relative_to.x() - border_size.width() +
- shadow_padding.right(),
- anchor_center_y - arrow_tip_y),
- border_size);
- }
-
- // No arrow bubble, center align with anchor.
- return position_relative_to.Center(border_size);
-}
-
-void BubbleBorder2::GetInsetsForArrowLocation(gfx::Insets* insets,
- ArrowLocation arrow_loc) const {
- int top = border_size_;
- int bottom = border_size_;
- int left = border_size_;
- int right = border_size_;
- switch (arrow_loc) {
- case TOP_LEFT:
- case TOP_RIGHT:
- top = std::max(top, arrow_height_);
- break;
-
- case BOTTOM_LEFT:
- case BOTTOM_RIGHT:
- bottom = std::max(bottom, arrow_height_);
- break;
-
- case LEFT_TOP:
- case LEFT_BOTTOM:
- left = std::max(left, arrow_height_);
- break;
-
- case RIGHT_TOP:
- case RIGHT_BOTTOM:
- right = std::max(right, arrow_height_);
- break;
-
- case NONE:
- case FLOAT:
- // Nothing to do.
- break;
- }
- insets->Set(top, left, bottom, right);
-}
-
-void BubbleBorder2::Paint(const views::View& view, gfx::Canvas* canvas) const {
- gfx::Insets insets;
- GetInsets(&insets);
-
- gfx::Rect content_bounds = view.bounds();
- content_bounds.Inset(insets);
-
- SkPath path;
- // Pads with 0.5 pixel since anti alias is used.
- BuildShape(content_bounds,
- arrow_location(),
- SkIntToScalar(GetArrowOffset()),
- SkDoubleToScalar(0.5),
- &path,
- corner_radius_,
- arrow_height_,
- arrow_width_);
-
- // Draw border and shadow. Note fill is needed to generate enough shadow.
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setStyle(SkPaint::kStrokeAndFill_Style);
- paint.setStrokeWidth(SkIntToScalar(border_size_));
- paint.setColor(border_color_);
- SkSafeUnref(paint.setLooper(gfx::CreateShadowDrawLooper(shadows_)));
- canvas->DrawPath(path, paint);
-
- // Pads with |border_size_| pixels to leave space for border lines.
- BuildShape(content_bounds,
- arrow_location(),
- SkIntToScalar(GetArrowOffset()),
- SkIntToScalar(border_size_),
- &path,
- corner_radius_,
- arrow_height_,
- arrow_width_);
- canvas->Save();
- canvas->ClipPath(path);
-
- // Use full bounds so that arrow is also painted.
- const gfx::Rect& bounds = view.bounds();
- PaintBackground(canvas, bounds);
-
- canvas->Restore();
-}
-
-} // namespace views
diff --git a/ui/views/bubble/bubble_border_2.h b/ui/views/bubble/bubble_border_2.h
deleted file mode 100644
index 46eafbc..0000000
--- a/ui/views/bubble/bubble_border_2.h
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (c) 2012 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 UI_VIEWS_BUBBLE_BUBBLE_BORDER_2_H_
-#define UI_VIEWS_BUBBLE_BUBBLE_BORDER_2_H_
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "ui/gfx/shadow_value.h"
-#include "ui/views/bubble/bubble_border.h"
-
-namespace views {
-
-// A BubbleBorder rendered with Skia drawing commands instead of images.
-class VIEWS_EXPORT BubbleBorder2 : public BubbleBorder {
- public:
- explicit BubbleBorder2(ArrowLocation arrow_location);
- virtual ~BubbleBorder2();
-
- // Given the |bubble_rect| that this border encloses, and the bounds of the
- // anchor view |anchor_view_rect|, compute the right offset to place the
- // arrow at shifting the |bubble_rect| to fit inside the display area if
- // needed. Returns the shifted |bubble_rect|.
- gfx::Rect ComputeOffsetAndUpdateBubbleRect(gfx::Rect bubble_rect,
- const gfx::Rect& anchor_view_rect);
-
- // Returns the path in |mask| that would be created if this border were to be
- // applied to the rect specified by |bounds|.
- void GetMask(const gfx::Rect& bounds, gfx::Path* mask) const;
-
- void set_offset(const gfx::Point& offset) { offset_ = offset; }
- const gfx::Point& offset() const { return offset_; }
-
- void set_corner_radius(int corner_radius) { corner_radius_ = corner_radius; }
- int corner_radius() const { return corner_radius_; }
-
- void set_border_size(int border_size) { border_size_ = border_size; }
- int border_size() const { return border_size_; }
-
- void set_arrow_height(int arrow_height) { arrow_height_ = arrow_height; }
- int arrow_height() const { return arrow_height_; }
-
- void set_arrow_width(int arrow_width) { arrow_width_ = arrow_width; }
- int arrow_width() const { return arrow_width_; }
-
- void SetShadow(gfx::ShadowValue shadow);
-
- // views::BubbleBorder overrides:
- virtual int GetBorderThickness() const OVERRIDE;
-
- protected:
- virtual void PaintBackground(gfx::Canvas* canvas,
- const gfx::Rect& bounds) const;
-
- private:
- // Gets arrow offset based on arrow location and |offset_|.
- int GetArrowOffset() const;
-
- // views::BubbleBorder overrides:
- virtual void GetInsets(gfx::Insets* insets) const OVERRIDE;
- virtual gfx::Rect GetBounds(const gfx::Rect& position_relative_to,
- const gfx::Size& contents_size) const OVERRIDE;
- virtual void GetInsetsForArrowLocation(
- gfx::Insets* insets,
- ArrowLocation arrow_loc) const OVERRIDE;
-
- // views::Border overrides:
- virtual void Paint(const View& view,
- gfx::Canvas* canvas) const OVERRIDE;
-
- int corner_radius_;
- int border_size_;
- int arrow_height_;
- int arrow_width_;
- SkColor background_color_;
- SkColor border_color_;
-
- // Offset in pixels by which the arrow is shifted relative the default middle
- // position. If the arrow is placed horizontally (at top or bottom), the |x_|
- // component of |offset_| specifies the offset, else, the |y_| component.
- gfx::Point offset_;
-
- gfx::ShadowValues shadows_;
-
- DISALLOW_COPY_AND_ASSIGN(BubbleBorder2);
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_BUBBLE_BUBBLE_BORDER_2_H_
diff --git a/ui/views/bubble/bubble_border_unittest.cc b/ui/views/bubble/bubble_border_unittest.cc
new file mode 100644
index 0000000..cb49030
--- /dev/null
+++ b/ui/views/bubble/bubble_border_unittest.cc
@@ -0,0 +1,205 @@
+// Copyright (c) 2012 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 "ui/views/bubble/bubble_border.h"
+
+#include "ui/views/test/views_test_base.h"
+
+namespace views {
+
+typedef ViewsTestBase BubbleBorderTest;
+
+TEST_F(BubbleBorderTest, GetMirroredArrow) {
+ // Horizontal mirroring.
+ EXPECT_EQ(BubbleBorder::TOP_RIGHT,
+ BubbleBorder::horizontal_mirror(BubbleBorder::TOP_LEFT));
+ EXPECT_EQ(BubbleBorder::TOP_LEFT,
+ BubbleBorder::horizontal_mirror(BubbleBorder::TOP_RIGHT));
+
+ EXPECT_EQ(BubbleBorder::BOTTOM_RIGHT,
+ BubbleBorder::horizontal_mirror(BubbleBorder::BOTTOM_LEFT));
+ EXPECT_EQ(BubbleBorder::BOTTOM_LEFT,
+ BubbleBorder::horizontal_mirror(BubbleBorder::BOTTOM_RIGHT));
+
+ EXPECT_EQ(BubbleBorder::RIGHT_TOP,
+ BubbleBorder::horizontal_mirror(BubbleBorder::LEFT_TOP));
+ EXPECT_EQ(BubbleBorder::LEFT_TOP,
+ BubbleBorder::horizontal_mirror(BubbleBorder::RIGHT_TOP));
+
+ EXPECT_EQ(BubbleBorder::RIGHT_BOTTOM,
+ BubbleBorder::horizontal_mirror(BubbleBorder::LEFT_BOTTOM));
+ EXPECT_EQ(BubbleBorder::LEFT_BOTTOM,
+ BubbleBorder::horizontal_mirror(BubbleBorder::RIGHT_BOTTOM));
+
+ EXPECT_EQ(BubbleBorder::TOP_CENTER,
+ BubbleBorder::horizontal_mirror(BubbleBorder::TOP_CENTER));
+ EXPECT_EQ(BubbleBorder::BOTTOM_CENTER,
+ BubbleBorder::horizontal_mirror(BubbleBorder::BOTTOM_CENTER));
+
+ EXPECT_EQ(BubbleBorder::RIGHT_CENTER,
+ BubbleBorder::horizontal_mirror(BubbleBorder::LEFT_CENTER));
+ EXPECT_EQ(BubbleBorder::LEFT_CENTER,
+ BubbleBorder::horizontal_mirror(BubbleBorder::RIGHT_CENTER));
+
+ EXPECT_EQ(BubbleBorder::NONE,
+ BubbleBorder::horizontal_mirror(BubbleBorder::NONE));
+ EXPECT_EQ(BubbleBorder::FLOAT,
+ BubbleBorder::horizontal_mirror(BubbleBorder::FLOAT));
+
+ // Vertical mirroring.
+ EXPECT_EQ(BubbleBorder::BOTTOM_LEFT,
+ BubbleBorder::vertical_mirror(BubbleBorder::TOP_LEFT));
+ EXPECT_EQ(BubbleBorder::BOTTOM_RIGHT,
+ BubbleBorder::vertical_mirror(BubbleBorder::TOP_RIGHT));
+
+ EXPECT_EQ(BubbleBorder::TOP_LEFT,
+ BubbleBorder::vertical_mirror(BubbleBorder::BOTTOM_LEFT));
+ EXPECT_EQ(BubbleBorder::TOP_RIGHT,
+ BubbleBorder::vertical_mirror(BubbleBorder::BOTTOM_RIGHT));
+
+ EXPECT_EQ(BubbleBorder::LEFT_BOTTOM,
+ BubbleBorder::vertical_mirror(BubbleBorder::LEFT_TOP));
+ EXPECT_EQ(BubbleBorder::RIGHT_BOTTOM,
+ BubbleBorder::vertical_mirror(BubbleBorder::RIGHT_TOP));
+
+ EXPECT_EQ(BubbleBorder::LEFT_TOP,
+ BubbleBorder::vertical_mirror(BubbleBorder::LEFT_BOTTOM));
+ EXPECT_EQ(BubbleBorder::RIGHT_TOP,
+ BubbleBorder::vertical_mirror(BubbleBorder::RIGHT_BOTTOM));
+
+ EXPECT_EQ(BubbleBorder::BOTTOM_CENTER,
+ BubbleBorder::vertical_mirror(BubbleBorder::TOP_CENTER));
+ EXPECT_EQ(BubbleBorder::TOP_CENTER,
+ BubbleBorder::vertical_mirror(BubbleBorder::BOTTOM_CENTER));
+
+ EXPECT_EQ(BubbleBorder::LEFT_CENTER,
+ BubbleBorder::vertical_mirror(BubbleBorder::LEFT_CENTER));
+ EXPECT_EQ(BubbleBorder::RIGHT_CENTER,
+ BubbleBorder::vertical_mirror(BubbleBorder::RIGHT_CENTER));
+
+ EXPECT_EQ(BubbleBorder::NONE,
+ BubbleBorder::vertical_mirror(BubbleBorder::NONE));
+ EXPECT_EQ(BubbleBorder::FLOAT,
+ BubbleBorder::vertical_mirror(BubbleBorder::FLOAT));
+}
+
+TEST_F(BubbleBorderTest, HasArrow) {
+ EXPECT_TRUE(BubbleBorder::has_arrow(BubbleBorder::TOP_LEFT));
+ EXPECT_TRUE(BubbleBorder::has_arrow(BubbleBorder::TOP_RIGHT));
+
+ EXPECT_TRUE(BubbleBorder::has_arrow(BubbleBorder::BOTTOM_LEFT));
+ EXPECT_TRUE(BubbleBorder::has_arrow(BubbleBorder::BOTTOM_RIGHT));
+
+ EXPECT_TRUE(BubbleBorder::has_arrow(BubbleBorder::LEFT_TOP));
+ EXPECT_TRUE(BubbleBorder::has_arrow(BubbleBorder::RIGHT_TOP));
+
+ EXPECT_TRUE(BubbleBorder::has_arrow(BubbleBorder::LEFT_BOTTOM));
+ EXPECT_TRUE(BubbleBorder::has_arrow(BubbleBorder::RIGHT_BOTTOM));
+
+ EXPECT_TRUE(BubbleBorder::has_arrow(BubbleBorder::TOP_CENTER));
+ EXPECT_TRUE(BubbleBorder::has_arrow(BubbleBorder::BOTTOM_CENTER));
+
+ EXPECT_TRUE(BubbleBorder::has_arrow(BubbleBorder::LEFT_CENTER));
+ EXPECT_TRUE(BubbleBorder::has_arrow(BubbleBorder::RIGHT_CENTER));
+
+ EXPECT_FALSE(BubbleBorder::has_arrow(BubbleBorder::NONE));
+ EXPECT_FALSE(BubbleBorder::has_arrow(BubbleBorder::FLOAT));
+}
+
+TEST_F(BubbleBorderTest, IsArrowOnLeft) {
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_left(BubbleBorder::TOP_LEFT));
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_left(BubbleBorder::TOP_RIGHT));
+
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_left(BubbleBorder::BOTTOM_LEFT));
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_left(BubbleBorder::BOTTOM_RIGHT));
+
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_left(BubbleBorder::LEFT_TOP));
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_left(BubbleBorder::RIGHT_TOP));
+
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_left(BubbleBorder::LEFT_BOTTOM));
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_left(BubbleBorder::RIGHT_BOTTOM));
+
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_left(BubbleBorder::TOP_CENTER));
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_left(BubbleBorder::BOTTOM_CENTER));
+
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_left(BubbleBorder::LEFT_CENTER));
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_left(BubbleBorder::RIGHT_CENTER));
+
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_left(BubbleBorder::NONE));
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_left(BubbleBorder::FLOAT));
+}
+
+TEST_F(BubbleBorderTest, IsArrowOnTop) {
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_top(BubbleBorder::TOP_LEFT));
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_top(BubbleBorder::TOP_RIGHT));
+
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_top(BubbleBorder::BOTTOM_LEFT));
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_top(BubbleBorder::BOTTOM_RIGHT));
+
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_top(BubbleBorder::LEFT_TOP));
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_top(BubbleBorder::RIGHT_TOP));
+
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_top(BubbleBorder::LEFT_BOTTOM));
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_top(BubbleBorder::RIGHT_BOTTOM));
+
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_top(BubbleBorder::TOP_CENTER));
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_top(BubbleBorder::BOTTOM_CENTER));
+
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_top(BubbleBorder::LEFT_CENTER));
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_top(BubbleBorder::RIGHT_CENTER));
+
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_top(BubbleBorder::NONE));
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_top(BubbleBorder::FLOAT));
+}
+
+TEST_F(BubbleBorderTest, IsArrowOnHorizontal) {
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_horizontal(BubbleBorder::TOP_LEFT));
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_horizontal(BubbleBorder::TOP_RIGHT));
+
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_horizontal(BubbleBorder::BOTTOM_LEFT));
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_horizontal(BubbleBorder::BOTTOM_RIGHT));
+
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_horizontal(BubbleBorder::LEFT_TOP));
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_horizontal(BubbleBorder::RIGHT_TOP));
+
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_horizontal(BubbleBorder::LEFT_BOTTOM));
+ EXPECT_FALSE(
+ BubbleBorder::is_arrow_on_horizontal(BubbleBorder::RIGHT_BOTTOM));
+
+ EXPECT_TRUE(BubbleBorder::is_arrow_on_horizontal(BubbleBorder::TOP_CENTER));
+ EXPECT_TRUE(
+ BubbleBorder::is_arrow_on_horizontal(BubbleBorder::BOTTOM_CENTER));
+
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_horizontal(BubbleBorder::LEFT_CENTER));
+ EXPECT_FALSE(
+ BubbleBorder::is_arrow_on_horizontal(BubbleBorder::RIGHT_CENTER));
+
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_horizontal(BubbleBorder::NONE));
+ EXPECT_FALSE(BubbleBorder::is_arrow_on_horizontal(BubbleBorder::FLOAT));
+}
+
+TEST_F(BubbleBorderTest, IsArrowAtCenter) {
+ EXPECT_FALSE(BubbleBorder::is_arrow_at_center(BubbleBorder::TOP_LEFT));
+ EXPECT_FALSE(BubbleBorder::is_arrow_at_center(BubbleBorder::TOP_RIGHT));
+
+ EXPECT_FALSE(BubbleBorder::is_arrow_at_center(BubbleBorder::BOTTOM_LEFT));
+ EXPECT_FALSE(BubbleBorder::is_arrow_at_center(BubbleBorder::BOTTOM_RIGHT));
+
+ EXPECT_FALSE(BubbleBorder::is_arrow_at_center(BubbleBorder::LEFT_TOP));
+ EXPECT_FALSE(BubbleBorder::is_arrow_at_center(BubbleBorder::RIGHT_TOP));
+
+ EXPECT_FALSE(BubbleBorder::is_arrow_at_center(BubbleBorder::LEFT_BOTTOM));
+ EXPECT_FALSE(BubbleBorder::is_arrow_at_center(BubbleBorder::RIGHT_BOTTOM));
+
+ EXPECT_TRUE(BubbleBorder::is_arrow_at_center(BubbleBorder::TOP_CENTER));
+ EXPECT_TRUE(BubbleBorder::is_arrow_at_center(BubbleBorder::BOTTOM_CENTER));
+
+ EXPECT_TRUE(BubbleBorder::is_arrow_at_center(BubbleBorder::LEFT_CENTER));
+ EXPECT_TRUE(BubbleBorder::is_arrow_at_center(BubbleBorder::RIGHT_CENTER));
+
+ EXPECT_FALSE(BubbleBorder::is_arrow_at_center(BubbleBorder::NONE));
+ EXPECT_FALSE(BubbleBorder::is_arrow_at_center(BubbleBorder::FLOAT));
+}
+
+} // namespace views
diff --git a/ui/views/bubble/bubble_delegate.cc b/ui/views/bubble/bubble_delegate.cc
index fa3a7b9..12c3798 100644
--- a/ui/views/bubble/bubble_delegate.cc
+++ b/ui/views/bubble/bubble_delegate.cc
@@ -111,13 +111,14 @@ BubbleDelegateView::BubbleDelegateView()
anchor_widget_(NULL),
move_with_anchor_(false),
arrow_location_(BubbleBorder::TOP_LEFT),
+ shadow_(BubbleBorder::NO_SHADOW),
color_(kBackgroundColor),
margins_(kDefaultMargin, kDefaultMargin, kDefaultMargin, kDefaultMargin),
original_opacity_(255),
border_widget_(NULL),
use_focusless_(false),
accept_events_(true),
- try_mirroring_arrow_(true),
+ adjust_if_offscreen_(true),
parent_window_(NULL) {
set_background(Background::CreateSolidBackground(color_));
AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
@@ -132,13 +133,14 @@ BubbleDelegateView::BubbleDelegateView(
anchor_widget_(NULL),
move_with_anchor_(false),
arrow_location_(arrow_location),
+ shadow_(BubbleBorder::NO_SHADOW),
color_(kBackgroundColor),
margins_(kDefaultMargin, kDefaultMargin, kDefaultMargin, kDefaultMargin),
original_opacity_(255),
border_widget_(NULL),
use_focusless_(false),
accept_events_(true),
- try_mirroring_arrow_(true),
+ adjust_if_offscreen_(true),
parent_window_(NULL) {
set_background(Background::CreateSolidBackground(color_));
AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
@@ -194,9 +196,7 @@ NonClientFrameView* BubbleDelegateView::CreateNonClientFrameView(
BubbleBorder::ArrowLocation arrow_loc = arrow_location();
if (base::i18n::IsRTL())
arrow_loc = BubbleBorder::horizontal_mirror(arrow_loc);
- // TODO(alicet): Expose the shadow option in BorderContentsView when we make
- // the fullscreen exit bubble use the new bubble code.
- BubbleBorder* border = new BubbleBorder(arrow_loc, BubbleBorder::NO_SHADOW);
+ BubbleBorder* border = new BubbleBorder(arrow_loc, shadow_);
border->set_background_color(color());
BubbleFrameView* frame_view = new BubbleFrameView(margins(), border);
frame_view->set_background(new BubbleBackground(border));
@@ -350,7 +350,7 @@ gfx::Rect BubbleDelegateView::GetBubbleBounds() {
// The argument rect has its origin at the bubble's arrow anchor point;
// its size is the preferred size of the bubble's client view (this view).
return GetBubbleFrameView()->GetUpdatedWindowBounds(GetAnchorRect(),
- GetPreferredSize(), try_mirroring_arrow_);
+ GetPreferredSize(), adjust_if_offscreen_);
}
#if defined(OS_WIN) && !defined(USE_AURA)
diff --git a/ui/views/bubble/bubble_delegate.h b/ui/views/bubble/bubble_delegate.h
index dd29da7..247202d 100644
--- a/ui/views/bubble/bubble_delegate.h
+++ b/ui/views/bubble/bubble_delegate.h
@@ -67,6 +67,9 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView,
arrow_location_ = arrow_location;
}
+ BubbleBorder::Shadow shadow() const { return shadow_; }
+ void set_shadow(BubbleBorder::Shadow shadow) { shadow_ = shadow; }
+
SkColor color() const { return color_; }
void set_color(SkColor color) { color_ = color; }
@@ -87,10 +90,8 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView,
bool accept_events() const { return accept_events_; }
void set_accept_events(bool accept_events) { accept_events_ = accept_events; }
- bool try_mirroring_arrow() const { return try_mirroring_arrow_; }
- void set_try_mirroring_arrow(bool try_mirroring_arrow) {
- try_mirroring_arrow_ = try_mirroring_arrow;
- }
+ bool adjust_if_offscreen() const { return adjust_if_offscreen_; }
+ void set_adjust_if_offscreen(bool adjust) { adjust_if_offscreen_ = adjust; }
// Get the arrow's anchor rect in screen space.
virtual gfx::Rect GetAnchorRect();
@@ -173,6 +174,9 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView,
// The arrow's location on the bubble.
BubbleBorder::ArrowLocation arrow_location_;
+ // Bubble border shadow to use.
+ BubbleBorder::Shadow shadow_;
+
// The background color of the bubble.
SkColor color_;
@@ -195,9 +199,9 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView,
// Specifies whether the popup accepts events or lets them pass through.
bool accept_events_;
- // If true (defaults to true), the arrow may be mirrored to fit the
- // bubble on screen better.
- bool try_mirroring_arrow_;
+ // If true (defaults to true), the arrow may be mirrored and moved to fit the
+ // bubble on screen better. It would be a no-op if the bubble has no arrow.
+ bool adjust_if_offscreen_;
// Parent native window of the bubble.
gfx::NativeView parent_window_;
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc
index 9ae1f8d..d164194 100644
--- a/ui/views/bubble/bubble_frame_view.cc
+++ b/ui/views/bubble/bubble_frame_view.cc
@@ -74,17 +74,22 @@ gfx::Size BubbleFrameView::GetPreferredSize() {
gfx::Rect BubbleFrameView::GetUpdatedWindowBounds(const gfx::Rect& anchor_rect,
gfx::Size client_size,
- bool try_mirroring_arrow) {
+ bool adjust_if_offscreen) {
// Give the contents a margin.
client_size.Enlarge(content_margins_.width(), content_margins_.height());
- if (try_mirroring_arrow) {
- // Try to mirror the anchoring if the bubble does not fit on the screen.
- MirrorArrowIfOffScreen(true, anchor_rect, client_size);
- MirrorArrowIfOffScreen(false, anchor_rect, client_size);
+ const BubbleBorder::ArrowLocation arrow = bubble_border_->arrow_location();
+ if (adjust_if_offscreen && BubbleBorder::has_arrow(arrow)) {
+ if (!bubble_border_->is_arrow_at_center(arrow)) {
+ // Try to mirror the anchoring if the bubble does not fit on the screen.
+ MirrorArrowIfOffScreen(true, anchor_rect, client_size);
+ MirrorArrowIfOffScreen(false, anchor_rect, client_size);
+ } else {
+ OffsetArrowIfOffScreen(anchor_rect, client_size);
+ }
}
- // Calculate the bounds with the arrow in its updated location.
+ // Calculate the bounds with the arrow in its updated location and offset.
return bubble_border_->GetBounds(anchor_rect, client_size);
}
@@ -124,4 +129,42 @@ void BubbleFrameView::MirrorArrowIfOffScreen(
}
}
+void BubbleFrameView::OffsetArrowIfOffScreen(const gfx::Rect& anchor_rect,
+ const gfx::Size& client_size) {
+ BubbleBorder::ArrowLocation arrow = bubble_border()->arrow_location();
+ DCHECK(BubbleBorder::is_arrow_at_center(arrow));
+
+ // Get the desired bubble bounds without adjustment.
+ bubble_border_->set_arrow_offset(0);
+ gfx::Rect window_bounds(bubble_border_->GetBounds(anchor_rect, client_size));
+
+ gfx::Rect monitor_rect(GetMonitorBounds(anchor_rect));
+ if (monitor_rect.IsEmpty() || monitor_rect.Contains(window_bounds))
+ return;
+
+ // Calculate off-screen adjustment.
+ const bool is_horizontal = BubbleBorder::is_arrow_on_horizontal(arrow);
+ int offscreen_adjust = 0;
+ if (is_horizontal) {
+ if (window_bounds.x() < monitor_rect.x())
+ offscreen_adjust = monitor_rect.x() - window_bounds.x();
+ else if (window_bounds.right() > monitor_rect.right())
+ offscreen_adjust = monitor_rect.right() - window_bounds.right();
+ } else {
+ if (window_bounds.y() < monitor_rect.y())
+ offscreen_adjust = monitor_rect.y() - window_bounds.y();
+ else if (window_bounds.bottom() > monitor_rect.bottom())
+ offscreen_adjust = monitor_rect.bottom() - window_bounds.bottom();
+ }
+
+ // For center arrows, arrows are moved in the opposite direction of
+ // |offscreen_adjust|, e.g. positive |offscreen_adjust| means bubble
+ // window needs to be moved to the right and that means we need to move arrow
+ // to the left, and that means negative offset.
+ bubble_border_->set_arrow_offset(
+ bubble_border_->GetArrowOffset(window_bounds.size()) - offscreen_adjust);
+ if (offscreen_adjust)
+ SchedulePaint();
+}
+
} // namespace views
diff --git a/ui/views/bubble/bubble_frame_view.h b/ui/views/bubble/bubble_frame_view.h
index 0f01428..b82fe8e 100644
--- a/ui/views/bubble/bubble_frame_view.h
+++ b/ui/views/bubble/bubble_frame_view.h
@@ -42,10 +42,10 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView {
// Given the size of the contents and the rect to point at, returns the bounds
// of the bubble window. The bubble's arrow location may change if the bubble
- // does not fit on the monitor and |try_mirroring_arrow| is true.
+ // does not fit on the monitor and |adjust_if_offscreen| is true.
gfx::Rect GetUpdatedWindowBounds(const gfx::Rect& anchor_rect,
gfx::Size client_size,
- bool try_mirroring_arrow);
+ bool adjust_if_offscreen);
void SetBubbleBorder(BubbleBorder* border);
@@ -63,6 +63,11 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView {
const gfx::Rect& anchor_rect,
const gfx::Size& client_size);
+ // Adjust the bubble's arrow offsets if the generated window bounds don't fit
+ // in the monitor bounds.
+ void OffsetArrowIfOffScreen(const gfx::Rect& anchor_rect,
+ const gfx::Size& client_size);
+
// The bubble border.
BubbleBorder* bubble_border_;
diff --git a/ui/views/bubble/bubble_frame_view_unittest.cc b/ui/views/bubble/bubble_frame_view_unittest.cc
index c6f1a5b..042e7ce 100644
--- a/ui/views/bubble/bubble_frame_view_unittest.cc
+++ b/ui/views/bubble/bubble_frame_view_unittest.cc
@@ -14,14 +14,14 @@ namespace views {
typedef ViewsTestBase BubbleFrameViewTest;
+namespace {
+
const BubbleBorder::ArrowLocation kArrow = BubbleBorder::TOP_LEFT;
const int kBubbleWidth = 200;
const int kBubbleHeight = 200;
const SkColor kBackgroundColor = SK_ColorRED;
const int kDefaultMargin = 6;
-namespace {
-
class SizedBubbleDelegateView : public BubbleDelegateView {
public:
SizedBubbleDelegateView();
@@ -114,7 +114,7 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBounds) {
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(100, 100, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
- true); // |try_mirroring_arrow|
+ true); // |adjust_if_offscreen|
EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow_location());
EXPECT_GT(window_bounds.x(), xposition);
EXPECT_GT(window_bounds.y(), 100 + 50 - 10); // -10 to roughly compensate for
@@ -125,7 +125,7 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBounds) {
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(100, 100, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
- true); // |try_mirroring_arrow|
+ true); // |adjust_if_offscreen|
EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow_location());
EXPECT_GT(window_bounds.x(), xposition);
EXPECT_GT(window_bounds.y(), 100 + 50 - 10); // -10 to roughly compensate for
@@ -136,7 +136,7 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBounds) {
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(100, 100, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
- true); // |try_mirroring_arrow|
+ true); // |adjust_if_offscreen|
EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow_location());
EXPECT_GT(window_bounds.x(), xposition);
EXPECT_GT(window_bounds.y(), 100 + 50 - 10); // -10 to roughly compensate for
@@ -147,7 +147,7 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBounds) {
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(100, 100, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
- true); // |try_mirroring_arrow|
+ true); // |adjust_if_offscreen|
EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow_location());
EXPECT_GT(window_bounds.x(), xposition);
EXPECT_GT(window_bounds.y(), 100 + 50 - 10); // -10 to roughly compensate for
@@ -158,7 +158,7 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBounds) {
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(900, 100, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
- true); // |try_mirroring_arrow|
+ true); // |adjust_if_offscreen|
EXPECT_EQ(BubbleBorder::TOP_RIGHT, frame.bubble_border()->arrow_location());
EXPECT_LT(window_bounds.x(), 900 + 50 - 500);
EXPECT_GT(window_bounds.y(), 100 + 50 - 10); // -10 to roughly compensate for
@@ -169,7 +169,7 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBounds) {
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(900, 100, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
- true); // |try_mirroring_arrow|
+ true); // |adjust_if_offscreen|
EXPECT_EQ(BubbleBorder::TOP_RIGHT, frame.bubble_border()->arrow_location());
EXPECT_LT(window_bounds.x(), 900 + 50 - 500);
EXPECT_GT(window_bounds.y(), 100 + 50 - 10); // -10 to roughly compensate for
@@ -180,7 +180,7 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBounds) {
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(900, 900, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
- true); // |try_mirroring_arrow|
+ true); // |adjust_if_offscreen|
EXPECT_EQ(BubbleBorder::BOTTOM_RIGHT,
frame.bubble_border()->arrow_location());
EXPECT_LT(window_bounds.x(), 900 + 50 - 500);
@@ -192,7 +192,7 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBounds) {
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(100, 900, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
- true); // |try_mirroring_arrow|
+ true); // |adjust_if_offscreen|
EXPECT_EQ(BubbleBorder::BOTTOM_LEFT, frame.bubble_border()->arrow_location());
// The window should be right aligned with the anchor_rect.
EXPECT_LT(window_bounds.x(), 900 + 50 - 500);
@@ -204,7 +204,7 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBounds) {
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(100, 900, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
- true); // |try_mirroring_arrow|
+ true); // |adjust_if_offscreen|
EXPECT_EQ(BubbleBorder::BOTTOM_LEFT, frame.bubble_border()->arrow_location());
// The window should be right aligned with the anchor_rect.
EXPECT_LT(window_bounds.x(), 900 + 50 - 500);
@@ -220,18 +220,18 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsMirroringFails) {
gfx::Rect window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(400, 100, 50, 50), // |anchor_rect|
gfx::Size(500, 700), // |client_size|
- true); // |try_mirroring_arrow|
+ true); // |adjust_if_offscreen|
EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow_location());
}
-// Test that the arrow will not be mirrored when |try_mirroring_arrow| is false.
+// Test that the arrow will not be mirrored when |adjust_if_offscreen| is false.
TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsDontTryMirror) {
TestBubbleFrameView frame;
frame.bubble_border()->set_arrow_location(BubbleBorder::TOP_RIGHT);
gfx::Rect window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(100, 900, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
- false); // |try_mirroring_arrow|
+ false); // |adjust_if_offscreen|
EXPECT_EQ(BubbleBorder::TOP_RIGHT, frame.bubble_border()->arrow_location());
// The coordinates should be pointing to anchor_rect from TOP_RIGHT.
EXPECT_LT(window_bounds.x(), 100 + 50 - 500);
@@ -239,4 +239,133 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsDontTryMirror) {
// arrow overlap.
}
+// Test that the center arrow is moved as needed to fit the screen.
+TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsCenterArrows) {
+ TestBubbleFrameView frame;
+ gfx::Rect window_bounds;
+
+ // Test that the bubble displays normally when it fits.
+ frame.bubble_border()->set_arrow_location(BubbleBorder::TOP_CENTER);
+ window_bounds = frame.GetUpdatedWindowBounds(
+ gfx::Rect(500, 100, 50, 50), // |anchor_rect|
+ gfx::Size(500, 500), // |client_size|
+ true); // |adjust_if_offscreen|
+ EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow_location());
+ EXPECT_EQ(window_bounds.x() + window_bounds.width() / 2, 525);
+
+ frame.bubble_border()->set_arrow_location(BubbleBorder::BOTTOM_CENTER);
+ window_bounds = frame.GetUpdatedWindowBounds(
+ gfx::Rect(500, 900, 50, 50), // |anchor_rect|
+ gfx::Size(500, 500), // |client_size|
+ true); // |adjust_if_offscreen|
+ EXPECT_EQ(BubbleBorder::BOTTOM_CENTER,
+ frame.bubble_border()->arrow_location());
+ EXPECT_EQ(window_bounds.x() + window_bounds.width() / 2, 525);
+
+ frame.bubble_border()->set_arrow_location(BubbleBorder::LEFT_CENTER);
+ window_bounds = frame.GetUpdatedWindowBounds(
+ gfx::Rect(100, 400, 50, 50), // |anchor_rect|
+ gfx::Size(500, 500), // |client_size|
+ true); // |adjust_if_offscreen|
+ EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow_location());
+ EXPECT_EQ(window_bounds.y() + window_bounds.height() / 2, 425);
+
+ frame.bubble_border()->set_arrow_location(BubbleBorder::RIGHT_CENTER);
+ window_bounds = frame.GetUpdatedWindowBounds(
+ gfx::Rect(900, 400, 50, 50), // |anchor_rect|
+ gfx::Size(500, 500), // |client_size|
+ true); // |adjust_if_offscreen|
+ EXPECT_EQ(BubbleBorder::RIGHT_CENTER,
+ frame.bubble_border()->arrow_location());
+ EXPECT_EQ(window_bounds.y() + window_bounds.height() / 2, 425);
+
+ // Test bubble not fitting left screen edge.
+ frame.bubble_border()->set_arrow_location(BubbleBorder::TOP_CENTER);
+ window_bounds = frame.GetUpdatedWindowBounds(
+ gfx::Rect(100, 100, 50, 50), // |anchor_rect|
+ gfx::Size(500, 500), // |client_size|
+ true); // |adjust_if_offscreen|
+ EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow_location());
+ EXPECT_EQ(window_bounds.x(), 0);
+ EXPECT_EQ(window_bounds.x() +
+ frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125);
+
+ frame.bubble_border()->set_arrow_location(BubbleBorder::BOTTOM_CENTER);
+ window_bounds = frame.GetUpdatedWindowBounds(
+ gfx::Rect(100, 900, 50, 50), // |anchor_rect|
+ gfx::Size(500, 500), // |client_size|
+ true); // |adjust_if_offscreen|
+ EXPECT_EQ(BubbleBorder::BOTTOM_CENTER,
+ frame.bubble_border()->arrow_location());
+ EXPECT_EQ(window_bounds.x(), 0);
+ EXPECT_EQ(window_bounds.x() +
+ frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125);
+
+ // Test bubble not fitting right screen edge.
+ frame.bubble_border()->set_arrow_location(BubbleBorder::TOP_CENTER);
+ window_bounds = frame.GetUpdatedWindowBounds(
+ gfx::Rect(900, 100, 50, 50), // |anchor_rect|
+ gfx::Size(500, 500), // |client_size|
+ true); // |adjust_if_offscreen|
+ EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow_location());
+ EXPECT_EQ(window_bounds.right(), 1000);
+ EXPECT_EQ(window_bounds.x() +
+ frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925);
+
+ frame.bubble_border()->set_arrow_location(BubbleBorder::BOTTOM_CENTER);
+ window_bounds = frame.GetUpdatedWindowBounds(
+ gfx::Rect(900, 900, 50, 50), // |anchor_rect|
+ gfx::Size(500, 500), // |client_size|
+ true); // |adjust_if_offscreen|
+ EXPECT_EQ(BubbleBorder::BOTTOM_CENTER,
+ frame.bubble_border()->arrow_location());
+ EXPECT_EQ(window_bounds.right(), 1000);
+ EXPECT_EQ(window_bounds.x() +
+ frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925);
+
+ // Test bubble not fitting top screen edge.
+ frame.bubble_border()->set_arrow_location(BubbleBorder::LEFT_CENTER);
+ window_bounds = frame.GetUpdatedWindowBounds(
+ gfx::Rect(100, 100, 50, 50), // |anchor_rect|
+ gfx::Size(500, 500), // |client_size|
+ true); // |adjust_if_offscreen|
+ EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow_location());
+ EXPECT_EQ(window_bounds.y(), 0);
+ EXPECT_EQ(window_bounds.y() +
+ frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125);
+
+ frame.bubble_border()->set_arrow_location(BubbleBorder::RIGHT_CENTER);
+ window_bounds = frame.GetUpdatedWindowBounds(
+ gfx::Rect(900, 100, 50, 50), // |anchor_rect|
+ gfx::Size(500, 500), // |client_size|
+ true); // |adjust_if_offscreen|
+ EXPECT_EQ(BubbleBorder::RIGHT_CENTER,
+ frame.bubble_border()->arrow_location());
+ EXPECT_EQ(window_bounds.y(), 0);
+ EXPECT_EQ(window_bounds.y() +
+ frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125);
+
+ // Test bubble not fitting bottom screen edge.
+ frame.bubble_border()->set_arrow_location(BubbleBorder::LEFT_CENTER);
+ window_bounds = frame.GetUpdatedWindowBounds(
+ gfx::Rect(100, 900, 50, 50), // |anchor_rect|
+ gfx::Size(500, 500), // |client_size|
+ true); // |adjust_if_offscreen|
+ EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow_location());
+ EXPECT_EQ(window_bounds.bottom(), 1000);
+ EXPECT_EQ(window_bounds.y() +
+ frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925);
+
+ frame.bubble_border()->set_arrow_location(BubbleBorder::RIGHT_CENTER);
+ window_bounds = frame.GetUpdatedWindowBounds(
+ gfx::Rect(900, 900, 50, 50), // |anchor_rect|
+ gfx::Size(500, 500), // |client_size|
+ true); // |adjust_if_offscreen|
+ EXPECT_EQ(BubbleBorder::RIGHT_CENTER,
+ frame.bubble_border()->arrow_location());
+ EXPECT_EQ(window_bounds.bottom(), 1000);
+ EXPECT_EQ(window_bounds.y() +
+ frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925);
+}
+
} // namespace views
diff --git a/ui/views/views.gyp b/ui/views/views.gyp
index 131cfac..2e30ab4 100644
--- a/ui/views/views.gyp
+++ b/ui/views/views.gyp
@@ -58,8 +58,6 @@
'bubble/bubble_delegate.h',
'bubble/bubble_frame_view.cc',
'bubble/bubble_frame_view.h',
- 'bubble/bubble_border_2.cc',
- 'bubble/bubble_border_2.h',
'button_drag_utils.cc',
'button_drag_utils.h',
'color_chooser/color_chooser_listener.h',
@@ -541,6 +539,7 @@
'sources': [
'accessible_pane_view_unittest.cc',
'animation/bounds_animator_unittest.cc',
+ 'bubble/bubble_border_unittest.cc',
'bubble/bubble_delegate_unittest.cc',
'bubble/bubble_frame_view_unittest.cc',
'controls/button/image_button_unittest.cc',