Refactored not to expose raw pointers on ProxyList class.
[chromium-blink-merge.git] / ash / shelf / overflow_bubble_view.cc
blob7dae49a536995cb82cc6ce59004807ea9afdd165
1 // Copyright 2013 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/shelf/overflow_bubble_view.h"
7 #include <algorithm>
9 #include "ash/root_window_controller.h"
10 #include "ash/shelf/shelf_constants.h"
11 #include "ash/shelf/shelf_layout_manager.h"
12 #include "ash/shelf/shelf_view.h"
13 #include "ash/shell.h"
14 #include "ash/shell_window_ids.h"
15 #include "ui/events/event.h"
16 #include "ui/gfx/geometry/insets.h"
17 #include "ui/gfx/screen.h"
18 #include "ui/views/bubble/bubble_frame_view.h"
19 #include "ui/views/widget/widget.h"
21 namespace ash {
22 namespace {
24 // Max bubble size to screen size ratio.
25 const float kMaxBubbleSizeToScreenRatio = 0.5f;
27 // Inner padding in pixels for shelf view inside bubble.
28 const int kPadding = 2;
30 // Padding space in pixels between ShelfView's left/top edge to its contents.
31 const int kShelfViewLeadingInset = 8;
33 } // namespace
35 OverflowBubbleView::OverflowBubbleView()
36 : shelf_view_(NULL) {
39 OverflowBubbleView::~OverflowBubbleView() {
42 void OverflowBubbleView::InitOverflowBubble(views::View* anchor,
43 ShelfView* shelf_view) {
44 // set_anchor_view needs to be called before GetShelfLayoutManager() can be
45 // called.
46 SetAnchorView(anchor);
47 set_arrow(GetBubbleArrow());
48 set_background(NULL);
49 set_color(SkColorSetARGB(kShelfBackgroundAlpha, 0, 0, 0));
50 set_margins(gfx::Insets(kPadding, kPadding, kPadding, kPadding));
51 // Overflow bubble should not get focus. If it get focus when it is shown,
52 // active state item is changed to running state.
53 set_can_activate(false);
55 // Makes bubble view has a layer and clip its children layers.
56 SetPaintToLayer(true);
57 SetFillsBoundsOpaquely(false);
58 layer()->SetMasksToBounds(true);
60 shelf_view_ = shelf_view;
61 AddChildView(shelf_view_);
63 set_parent_window(Shell::GetContainer(
64 anchor->GetWidget()->GetNativeWindow()->GetRootWindow(),
65 kShellWindowId_ShelfBubbleContainer));
66 views::BubbleDelegateView::CreateBubble(this);
69 bool OverflowBubbleView::IsHorizontalAlignment() const {
70 ShelfLayoutManager* shelf_layout_manager = GetShelfLayoutManager();
71 return shelf_layout_manager ? shelf_layout_manager->IsHorizontalAlignment()
72 : false;
75 const gfx::Size OverflowBubbleView::GetContentsSize() const {
76 return static_cast<views::View*>(shelf_view_)->GetPreferredSize();
79 // Gets arrow location based on shelf alignment.
80 views::BubbleBorder::Arrow OverflowBubbleView::GetBubbleArrow() const {
81 ShelfLayoutManager* shelf_layout_manager = GetShelfLayoutManager();
82 return shelf_layout_manager ?
83 shelf_layout_manager->SelectValueForShelfAlignment(
84 views::BubbleBorder::BOTTOM_LEFT,
85 views::BubbleBorder::LEFT_TOP,
86 views::BubbleBorder::RIGHT_TOP,
87 views::BubbleBorder::TOP_LEFT) :
88 views::BubbleBorder::NONE;
91 void OverflowBubbleView::ScrollByXOffset(int x_offset) {
92 const gfx::Rect visible_bounds(GetContentsBounds());
93 const gfx::Size contents_size(GetContentsSize());
95 DCHECK_GE(contents_size.width(), visible_bounds.width());
96 int x = std::min(contents_size.width() - visible_bounds.width(),
97 std::max(0, scroll_offset_.x() + x_offset));
98 scroll_offset_.set_x(x);
101 void OverflowBubbleView::ScrollByYOffset(int y_offset) {
102 const gfx::Rect visible_bounds(GetContentsBounds());
103 const gfx::Size contents_size(GetContentsSize());
105 DCHECK_GE(contents_size.width(), visible_bounds.width());
106 int y = std::min(contents_size.height() - visible_bounds.height(),
107 std::max(0, scroll_offset_.y() + y_offset));
108 scroll_offset_.set_y(y);
111 gfx::Size OverflowBubbleView::GetPreferredSize() const {
112 gfx::Size preferred_size = GetContentsSize();
114 const gfx::Rect monitor_rect = Shell::GetScreen()->GetDisplayNearestPoint(
115 GetAnchorRect().CenterPoint()).work_area();
116 if (!monitor_rect.IsEmpty()) {
117 if (IsHorizontalAlignment()) {
118 preferred_size.set_width(std::min(
119 preferred_size.width(),
120 static_cast<int>(monitor_rect.width() *
121 kMaxBubbleSizeToScreenRatio)));
122 } else {
123 preferred_size.set_height(std::min(
124 preferred_size.height(),
125 static_cast<int>(monitor_rect.height() *
126 kMaxBubbleSizeToScreenRatio)));
130 return preferred_size;
133 void OverflowBubbleView::Layout() {
134 shelf_view_->SetBoundsRect(gfx::Rect(
135 gfx::PointAtOffsetFromOrigin(-scroll_offset_), GetContentsSize()));
138 void OverflowBubbleView::ChildPreferredSizeChanged(views::View* child) {
139 // When contents size is changed, ContentsBounds should be updated before
140 // calculating scroll offset.
141 SizeToContents();
143 // Ensures |shelf_view_| is still visible.
144 if (IsHorizontalAlignment())
145 ScrollByXOffset(0);
146 else
147 ScrollByYOffset(0);
148 Layout();
151 bool OverflowBubbleView::OnMouseWheel(const ui::MouseWheelEvent& event) {
152 // The MouseWheelEvent was changed to support both X and Y offsets
153 // recently, but the behavior of this function was retained to continue
154 // using Y offsets only. Might be good to simply scroll in both
155 // directions as in OverflowBubbleView::OnScrollEvent.
156 if (IsHorizontalAlignment())
157 ScrollByXOffset(-event.y_offset());
158 else
159 ScrollByYOffset(-event.y_offset());
160 Layout();
162 return true;
165 ShelfLayoutManager* OverflowBubbleView::GetShelfLayoutManager() const {
166 return GetAnchorView() ? ShelfLayoutManager::ForShelf(
167 GetAnchorView()->GetWidget()->GetNativeView())
168 : NULL;
171 void OverflowBubbleView::OnScrollEvent(ui::ScrollEvent* event) {
172 ScrollByXOffset(-event->x_offset());
173 ScrollByYOffset(-event->y_offset());
174 Layout();
175 event->SetHandled();
178 gfx::Rect OverflowBubbleView::GetBubbleBounds() {
179 views::BubbleBorder* border = GetBubbleFrameView()->bubble_border();
180 gfx::Insets bubble_insets = border->GetInsets();
182 const int border_size =
183 views::BubbleBorder::is_arrow_on_horizontal(arrow()) ?
184 bubble_insets.left() : bubble_insets.top();
185 const int arrow_offset = border_size + kPadding + kShelfViewLeadingInset +
186 kShelfSize / 2;
188 const gfx::Size content_size = GetPreferredSize();
189 border->set_arrow_offset(arrow_offset);
191 const gfx::Rect anchor_rect = GetAnchorRect();
192 gfx::Rect bubble_rect = GetBubbleFrameView()->GetUpdatedWindowBounds(
193 anchor_rect,
194 content_size,
195 false);
197 gfx::Rect monitor_rect = Shell::GetScreen()->GetDisplayNearestPoint(
198 anchor_rect.CenterPoint()).work_area();
200 int offset = 0;
201 if (views::BubbleBorder::is_arrow_on_horizontal(arrow())) {
202 if (bubble_rect.x() < monitor_rect.x())
203 offset = monitor_rect.x() - bubble_rect.x();
204 else if (bubble_rect.right() > monitor_rect.right())
205 offset = monitor_rect.right() - bubble_rect.right();
207 bubble_rect.Offset(offset, 0);
208 border->set_arrow_offset(anchor_rect.CenterPoint().x() - bubble_rect.x());
209 } else {
210 if (bubble_rect.y() < monitor_rect.y())
211 offset = monitor_rect.y() - bubble_rect.y();
212 else if (bubble_rect.bottom() > monitor_rect.bottom())
213 offset = monitor_rect.bottom() - bubble_rect.bottom();
215 bubble_rect.Offset(0, offset);
216 border->set_arrow_offset(anchor_rect.CenterPoint().y() - bubble_rect.y());
219 GetBubbleFrameView()->SchedulePaint();
220 return bubble_rect;
223 } // namespace ash