Consolidate bubble border code.
[chromium-blink-merge.git] / ash / launcher / overflow_bubble.cc
blob6489583e3f5a2f3937dcce7991376c85415cd5c2
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/launcher/overflow_bubble.h"
7 #include <algorithm>
9 #include "ash/launcher/launcher_types.h"
10 #include "ash/launcher/launcher_view.h"
11 #include "ui/gfx/insets.h"
12 #include "ui/gfx/screen.h"
13 #include "ui/views/bubble/bubble_delegate.h"
14 #include "ui/views/bubble/bubble_frame_view.h"
15 #include "ui/views/widget/widget.h"
17 namespace ash {
18 namespace internal {
20 namespace {
22 // Max bubble size to screen size ratio.
23 const float kMaxBubbleSizeToScreenRatio = 0.5f;
25 // Inner padding in pixels for launcher view inside bubble.
26 const int kPadding = 2;
28 // Padding space in pixels between LauncherView's left/top edge to its contents.
29 const int kLauncherViewLeadingInset = 8;
31 // Gets arrow location based on shelf alignment.
32 views::BubbleBorder::ArrowLocation GetBubbleArrowLocation(
33 ShelfAlignment shelf_alignment) {
34 switch (shelf_alignment) {
35 case ash::SHELF_ALIGNMENT_BOTTOM:
36 return views::BubbleBorder::BOTTOM_LEFT;
37 case ash::SHELF_ALIGNMENT_LEFT:
38 return views::BubbleBorder::LEFT_TOP;
39 case ash::SHELF_ALIGNMENT_RIGHT:
40 return views::BubbleBorder::RIGHT_TOP;
41 default:
42 NOTREACHED() << "Unknown shelf alignment " << shelf_alignment;
43 return views::BubbleBorder::BOTTOM_LEFT;
47 ////////////////////////////////////////////////////////////////////////////////
48 // OverflowBubbleView
49 // OverflowBubbleView hosts a LauncherView to display overflown items.
51 class OverflowBubbleView : public views::BubbleDelegateView {
52 public:
53 OverflowBubbleView();
54 virtual ~OverflowBubbleView();
56 void InitOverflowBubble(LauncherDelegate* delegate,
57 LauncherModel* model,
58 views::View* anchor,
59 ShelfAlignment shelf_alignment,
60 int overflow_start_index);
62 private:
63 bool is_horizontal_alignment() const {
64 return shelf_alignment_ == SHELF_ALIGNMENT_BOTTOM;
67 const gfx::Size GetContentsSize() const {
68 return static_cast<views::View*>(launcher_view_)->GetPreferredSize();
71 void ScrollByXOffset(int x_offset);
72 void ScrollByYOffset(int y_offset);
74 // views::View overrides:
75 virtual gfx::Size GetPreferredSize() OVERRIDE;
76 virtual void Layout() OVERRIDE;
77 virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE;
78 virtual bool OnMouseWheel(const ui::MouseWheelEvent& event) OVERRIDE;
79 virtual bool OnScrollEvent(const ui::ScrollEvent& event) OVERRIDE;
81 // views::BubbleDelegate overrides:
82 virtual gfx::Rect GetBubbleBounds() OVERRIDE;
84 ShelfAlignment shelf_alignment_;
85 LauncherView* launcher_view_; // Owned by views hierarchy.
86 gfx::Point scroll_offset_;
88 DISALLOW_COPY_AND_ASSIGN(OverflowBubbleView);
91 OverflowBubbleView::OverflowBubbleView()
92 : shelf_alignment_(SHELF_ALIGNMENT_BOTTOM),
93 launcher_view_(NULL) {
96 OverflowBubbleView::~OverflowBubbleView() {
99 void OverflowBubbleView::InitOverflowBubble(LauncherDelegate* delegate,
100 LauncherModel* model,
101 views::View* anchor,
102 ShelfAlignment shelf_alignment,
103 int overflow_start_index) {
104 shelf_alignment_ = shelf_alignment;
106 // Makes bubble view has a layer and clip its children layers.
107 SetPaintToLayer(true);
108 SetFillsBoundsOpaquely(false);
109 layer()->SetMasksToBounds(true);
111 launcher_view_ = new LauncherView(model, delegate, NULL);
112 launcher_view_->set_first_visible_index(overflow_start_index);
113 launcher_view_->set_leading_inset(kLauncherViewLeadingInset);
114 launcher_view_->Init();
115 launcher_view_->SetAlignment(shelf_alignment);
116 AddChildView(launcher_view_);
118 set_anchor_view(anchor);
119 set_arrow_location(GetBubbleArrowLocation(shelf_alignment));
120 set_background(NULL);
121 set_color(SkColorSetARGB(kLauncherBackgroundAlpha, 0, 0, 0));
122 set_margins(gfx::Insets(kPadding, kPadding, kPadding, kPadding));
123 set_move_with_anchor(true);
124 views::BubbleDelegateView::CreateBubble(this);
127 void OverflowBubbleView::ScrollByXOffset(int x_offset) {
128 const gfx::Rect visible_bounds(GetContentsBounds());
129 const gfx::Size contents_size(GetContentsSize());
131 int x = std::min(contents_size.width() - visible_bounds.width(),
132 std::max(0, scroll_offset_.x() + x_offset));
133 scroll_offset_.set_x(x);
136 void OverflowBubbleView::ScrollByYOffset(int y_offset) {
137 const gfx::Rect visible_bounds(GetContentsBounds());
138 const gfx::Size contents_size(GetContentsSize());
140 int y = std::min(contents_size.height() - visible_bounds.height(),
141 std::max(0, scroll_offset_.y() + y_offset));
142 scroll_offset_.set_y(y);
145 gfx::Size OverflowBubbleView::GetPreferredSize() {
146 gfx::Size preferred_size = GetContentsSize();
148 const gfx::Rect monitor_rect = gfx::Screen::GetDisplayNearestPoint(
149 GetAnchorRect().CenterPoint()).work_area();
150 if (!monitor_rect.IsEmpty()) {
151 if (is_horizontal_alignment()) {
152 preferred_size.set_width(std::min(
153 preferred_size.width(),
154 static_cast<int>(monitor_rect.width() *
155 kMaxBubbleSizeToScreenRatio)));
156 } else {
157 preferred_size.set_height(std::min(
158 preferred_size.height(),
159 static_cast<int>(monitor_rect.height() *
160 kMaxBubbleSizeToScreenRatio)));
164 return preferred_size;
167 void OverflowBubbleView::Layout() {
168 const gfx::Point origin(-scroll_offset_.x(), -scroll_offset_.y());
169 launcher_view_->SetBoundsRect(gfx::Rect(origin, GetContentsSize()));
172 void OverflowBubbleView::ChildPreferredSizeChanged(views::View* child) {
173 // Ensures |launch_view_| is still visible.
174 ScrollByXOffset(0);
175 ScrollByYOffset(0);
176 Layout();
178 SizeToContents();
181 bool OverflowBubbleView::OnMouseWheel(const ui::MouseWheelEvent& event) {
182 if (is_horizontal_alignment())
183 ScrollByXOffset(-event.offset());
184 else
185 ScrollByYOffset(-event.offset());
186 Layout();
188 return true;
191 bool OverflowBubbleView::OnScrollEvent(const ui::ScrollEvent& event) {
192 ScrollByXOffset(-event.x_offset());
193 ScrollByYOffset(-event.y_offset());
194 Layout();
195 return true;
198 gfx::Rect OverflowBubbleView::GetBubbleBounds() {
199 views::BubbleBorder* border = GetBubbleFrameView()->bubble_border();
200 gfx::Insets bubble_insets;
201 border->GetInsets(&bubble_insets);
203 const int border_size =
204 views::BubbleBorder::is_arrow_on_horizontal(arrow_location()) ?
205 bubble_insets.left() : bubble_insets.top();
206 const int arrow_offset = border_size + kPadding + kLauncherViewLeadingInset +
207 kLauncherPreferredSize / 2;
209 const gfx::Size content_size = GetPreferredSize();
210 border->set_arrow_offset(arrow_offset);
212 const gfx::Rect anchor_rect = GetAnchorRect();
213 gfx::Rect bubble_rect = GetBubbleFrameView()->GetUpdatedWindowBounds(
214 anchor_rect,
215 content_size,
216 false);
218 gfx::Rect monitor_rect = gfx::Screen::GetDisplayNearestPoint(
219 anchor_rect.CenterPoint()).work_area();
221 int offset = 0;
222 if (views::BubbleBorder::is_arrow_on_horizontal(arrow_location())) {
223 if (bubble_rect.x() < monitor_rect.x())
224 offset = monitor_rect.x() - bubble_rect.x();
225 else if (bubble_rect.right() > monitor_rect.right())
226 offset = monitor_rect.right() - bubble_rect.right();
228 bubble_rect.Offset(offset, 0);
229 border->set_arrow_offset(anchor_rect.CenterPoint().x() - bubble_rect.x());
230 } else {
231 if (bubble_rect.y() < monitor_rect.y())
232 offset = monitor_rect.y() - bubble_rect.y();
233 else if (bubble_rect.bottom() > monitor_rect.bottom())
234 offset = monitor_rect.bottom() - bubble_rect.bottom();
236 bubble_rect.Offset(0, offset);
237 border->set_arrow_offset(anchor_rect.CenterPoint().y() - bubble_rect.y());
240 GetBubbleFrameView()->SchedulePaint();
241 return bubble_rect;
244 } // namespace
246 OverflowBubble::OverflowBubble()
247 : bubble_(NULL) {
250 OverflowBubble::~OverflowBubble() {
251 Hide();
254 void OverflowBubble::Show(LauncherDelegate* delegate,
255 LauncherModel* model,
256 views::View* anchor,
257 ShelfAlignment shelf_alignment,
258 int overflow_start_index) {
259 Hide();
261 OverflowBubbleView* bubble_view = new OverflowBubbleView();
262 bubble_view->InitOverflowBubble(delegate,
263 model,
264 anchor,
265 shelf_alignment,
266 overflow_start_index);
268 bubble_ = bubble_view;
269 bubble_->GetWidget()->AddObserver(this);
270 bubble_->GetWidget()->Show();
273 void OverflowBubble::Hide() {
274 if (!IsShowing())
275 return;
277 bubble_->GetWidget()->RemoveObserver(this);
278 bubble_->GetWidget()->Close();
279 bubble_ = NULL;
282 void OverflowBubble::OnWidgetClosing(views::Widget* widget) {
283 DCHECK(widget == bubble_->GetWidget());
284 bubble_ = NULL;
287 } // namespace internal
288 } // namespace ash