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"
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"
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
;
42 NOTREACHED() << "Unknown shelf alignment " << shelf_alignment
;
43 return views::BubbleBorder::BOTTOM_LEFT
;
47 ////////////////////////////////////////////////////////////////////////////////
49 // OverflowBubbleView hosts a LauncherView to display overflown items.
51 class OverflowBubbleView
: public views::BubbleDelegateView
{
54 virtual ~OverflowBubbleView();
56 void InitOverflowBubble(LauncherDelegate
* delegate
,
59 ShelfAlignment shelf_alignment
,
60 int overflow_start_index
);
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
,
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
)));
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.
181 bool OverflowBubbleView::OnMouseWheel(const ui::MouseWheelEvent
& event
) {
182 if (is_horizontal_alignment())
183 ScrollByXOffset(-event
.offset());
185 ScrollByYOffset(-event
.offset());
191 bool OverflowBubbleView::OnScrollEvent(const ui::ScrollEvent
& event
) {
192 ScrollByXOffset(-event
.x_offset());
193 ScrollByYOffset(-event
.y_offset());
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(
218 gfx::Rect monitor_rect
= gfx::Screen::GetDisplayNearestPoint(
219 anchor_rect
.CenterPoint()).work_area();
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());
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();
246 OverflowBubble::OverflowBubble()
250 OverflowBubble::~OverflowBubble() {
254 void OverflowBubble::Show(LauncherDelegate
* delegate
,
255 LauncherModel
* model
,
257 ShelfAlignment shelf_alignment
,
258 int overflow_start_index
) {
261 OverflowBubbleView
* bubble_view
= new OverflowBubbleView();
262 bubble_view
->InitOverflowBubble(delegate
,
266 overflow_start_index
);
268 bubble_
= bubble_view
;
269 bubble_
->GetWidget()->AddObserver(this);
270 bubble_
->GetWidget()->Show();
273 void OverflowBubble::Hide() {
277 bubble_
->GetWidget()->RemoveObserver(this);
278 bubble_
->GetWidget()->Close();
282 void OverflowBubble::OnWidgetClosing(views::Widget
* widget
) {
283 DCHECK(widget
== bubble_
->GetWidget());
287 } // namespace internal