OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "ash/shelf/overflow_bubble_view.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "ash/common/material_design/material_design_controller.h" | |
10 #include "ash/common/shelf/shelf_constants.h" | |
11 #include "ash/common/shell_window_ids.h" | |
12 #include "ash/root_window_controller.h" | |
13 #include "ash/shelf/shelf.h" | |
14 #include "ash/shelf/shelf_view.h" | |
15 #include "ash/shell.h" | |
16 #include "ui/display/display.h" | |
17 #include "ui/display/screen.h" | |
18 #include "ui/events/event.h" | |
19 #include "ui/gfx/geometry/insets.h" | |
20 #include "ui/views/bubble/bubble_frame_view.h" | |
21 #include "ui/views/widget/widget.h" | |
22 | |
23 namespace ash { | |
24 namespace { | |
25 | |
26 // Max bubble size to screen size ratio. | |
27 const float kMaxBubbleSizeToScreenRatio = 0.5f; | |
28 | |
29 // Inner padding in pixels for shelf view inside bubble. | |
30 const int kPadding = 2; | |
31 | |
32 // Padding space in pixels between ShelfView's left/top edge to its contents. | |
33 const int kShelfViewLeadingInset = 8; | |
34 | |
35 } // namespace | |
36 | |
37 OverflowBubbleView::OverflowBubbleView() : shelf_view_(NULL) {} | |
38 | |
39 OverflowBubbleView::~OverflowBubbleView() {} | |
40 | |
41 void OverflowBubbleView::InitOverflowBubble(views::View* anchor, | |
42 ShelfView* shelf_view) { | |
43 shelf_view_ = shelf_view; | |
44 | |
45 SetAnchorView(anchor); | |
46 set_arrow(GetBubbleArrow()); | |
47 set_mirror_arrow_in_rtl(false); | |
48 set_background(NULL); | |
49 SkColor color = MaterialDesignController::IsShelfMaterial() | |
50 ? kShelfBaseColor | |
51 : SkColorSetA(kShelfBaseColor, | |
52 GetShelfConstant(SHELF_BACKGROUND_ALPHA)); | |
53 set_color(color); | |
54 set_margins(gfx::Insets(kPadding, kPadding, kPadding, kPadding)); | |
55 // Overflow bubble should not get focus. If it get focus when it is shown, | |
56 // active state item is changed to running state. | |
57 set_can_activate(false); | |
58 | |
59 // Makes bubble view has a layer and clip its children layers. | |
60 SetPaintToLayer(true); | |
61 layer()->SetFillsBoundsOpaquely(false); | |
62 layer()->SetMasksToBounds(true); | |
63 | |
64 set_parent_window(Shell::GetContainer( | |
65 anchor->GetWidget()->GetNativeWindow()->GetRootWindow(), | |
66 kShellWindowId_ShelfBubbleContainer)); | |
67 views::BubbleDialogDelegateView::CreateBubble(this); | |
68 AddChildView(shelf_view_); | |
69 } | |
70 | |
71 bool OverflowBubbleView::IsHorizontalAlignment() const { | |
72 return shelf_view_ ? shelf_view_->shelf()->IsHorizontalAlignment() : false; | |
73 } | |
74 | |
75 const gfx::Size OverflowBubbleView::GetContentsSize() const { | |
76 return static_cast<views::View*>(shelf_view_)->GetPreferredSize(); | |
77 } | |
78 | |
79 // Gets arrow location based on shelf alignment. | |
80 views::BubbleBorder::Arrow OverflowBubbleView::GetBubbleArrow() const { | |
81 if (!shelf_view_) | |
82 return views::BubbleBorder::NONE; | |
83 return shelf_view_->shelf()->SelectValueForShelfAlignment( | |
84 views::BubbleBorder::BOTTOM_LEFT, views::BubbleBorder::LEFT_TOP, | |
85 views::BubbleBorder::RIGHT_TOP); | |
86 } | |
87 | |
88 void OverflowBubbleView::ScrollByXOffset(int x_offset) { | |
89 const gfx::Rect visible_bounds(GetContentsBounds()); | |
90 const gfx::Size contents_size(GetContentsSize()); | |
91 | |
92 DCHECK_GE(contents_size.width(), visible_bounds.width()); | |
93 int x = std::min(contents_size.width() - visible_bounds.width(), | |
94 std::max(0, scroll_offset_.x() + x_offset)); | |
95 scroll_offset_.set_x(x); | |
96 } | |
97 | |
98 void OverflowBubbleView::ScrollByYOffset(int y_offset) { | |
99 const gfx::Rect visible_bounds(GetContentsBounds()); | |
100 const gfx::Size contents_size(GetContentsSize()); | |
101 | |
102 DCHECK_GE(contents_size.width(), visible_bounds.width()); | |
103 int y = std::min(contents_size.height() - visible_bounds.height(), | |
104 std::max(0, scroll_offset_.y() + y_offset)); | |
105 scroll_offset_.set_y(y); | |
106 } | |
107 | |
108 gfx::Size OverflowBubbleView::GetPreferredSize() const { | |
109 gfx::Size preferred_size = GetContentsSize(); | |
110 | |
111 const gfx::Rect monitor_rect = | |
112 display::Screen::GetScreen() | |
113 ->GetDisplayNearestPoint(GetAnchorRect().CenterPoint()) | |
114 .work_area(); | |
115 if (!monitor_rect.IsEmpty()) { | |
116 if (IsHorizontalAlignment()) { | |
117 preferred_size.set_width( | |
118 std::min(preferred_size.width(), | |
119 static_cast<int>(monitor_rect.width() * | |
120 kMaxBubbleSizeToScreenRatio))); | |
121 } else { | |
122 preferred_size.set_height( | |
123 std::min(preferred_size.height(), | |
124 static_cast<int>(monitor_rect.height() * | |
125 kMaxBubbleSizeToScreenRatio))); | |
126 } | |
127 } | |
128 | |
129 return preferred_size; | |
130 } | |
131 | |
132 void OverflowBubbleView::Layout() { | |
133 shelf_view_->SetBoundsRect(gfx::Rect( | |
134 gfx::PointAtOffsetFromOrigin(-scroll_offset_), GetContentsSize())); | |
135 } | |
136 | |
137 void OverflowBubbleView::ChildPreferredSizeChanged(views::View* child) { | |
138 // When contents size is changed, ContentsBounds should be updated before | |
139 // calculating scroll offset. | |
140 SizeToContents(); | |
141 | |
142 // Ensures |shelf_view_| is still visible. | |
143 if (IsHorizontalAlignment()) | |
144 ScrollByXOffset(0); | |
145 else | |
146 ScrollByYOffset(0); | |
147 Layout(); | |
148 } | |
149 | |
150 bool OverflowBubbleView::OnMouseWheel(const ui::MouseWheelEvent& event) { | |
151 // The MouseWheelEvent was changed to support both X and Y offsets | |
152 // recently, but the behavior of this function was retained to continue | |
153 // using Y offsets only. Might be good to simply scroll in both | |
154 // directions as in OverflowBubbleView::OnScrollEvent. | |
155 if (IsHorizontalAlignment()) | |
156 ScrollByXOffset(-event.y_offset()); | |
157 else | |
158 ScrollByYOffset(-event.y_offset()); | |
159 Layout(); | |
160 | |
161 return true; | |
162 } | |
163 | |
164 void OverflowBubbleView::OnScrollEvent(ui::ScrollEvent* event) { | |
165 ScrollByXOffset(-event->x_offset()); | |
166 ScrollByYOffset(-event->y_offset()); | |
167 Layout(); | |
168 event->SetHandled(); | |
169 } | |
170 | |
171 int OverflowBubbleView::GetDialogButtons() const { | |
172 return ui::DIALOG_BUTTON_NONE; | |
173 } | |
174 | |
175 gfx::Rect OverflowBubbleView::GetBubbleBounds() { | |
176 views::BubbleBorder* border = GetBubbleFrameView()->bubble_border(); | |
177 gfx::Insets bubble_insets = border->GetInsets(); | |
178 | |
179 const int border_size = views::BubbleBorder::is_arrow_on_horizontal(arrow()) | |
180 ? bubble_insets.left() | |
181 : bubble_insets.top(); | |
182 const int arrow_offset = border_size + kPadding + kShelfViewLeadingInset + | |
183 GetShelfConstant(SHELF_SIZE) / 2; | |
184 | |
185 const gfx::Size content_size = GetPreferredSize(); | |
186 border->set_arrow_offset(arrow_offset); | |
187 | |
188 const gfx::Rect anchor_rect = GetAnchorRect(); | |
189 gfx::Rect bubble_rect = GetBubbleFrameView()->GetUpdatedWindowBounds( | |
190 anchor_rect, content_size, false); | |
191 | |
192 gfx::Rect monitor_rect = | |
193 display::Screen::GetScreen() | |
194 ->GetDisplayNearestPoint(anchor_rect.CenterPoint()) | |
195 .work_area(); | |
196 | |
197 int offset = 0; | |
198 if (views::BubbleBorder::is_arrow_on_horizontal(arrow())) { | |
199 if (bubble_rect.x() < monitor_rect.x()) | |
200 offset = monitor_rect.x() - bubble_rect.x(); | |
201 else if (bubble_rect.right() > monitor_rect.right()) | |
202 offset = monitor_rect.right() - bubble_rect.right(); | |
203 | |
204 bubble_rect.Offset(offset, 0); | |
205 border->set_arrow_offset(anchor_rect.CenterPoint().x() - bubble_rect.x()); | |
206 } else { | |
207 if (bubble_rect.y() < monitor_rect.y()) | |
208 offset = monitor_rect.y() - bubble_rect.y(); | |
209 else if (bubble_rect.bottom() > monitor_rect.bottom()) | |
210 offset = monitor_rect.bottom() - bubble_rect.bottom(); | |
211 | |
212 bubble_rect.Offset(0, offset); | |
213 border->set_arrow_offset(anchor_rect.CenterPoint().y() - bubble_rect.y()); | |
214 } | |
215 | |
216 GetBubbleFrameView()->SchedulePaint(); | |
217 return bubble_rect; | |
218 } | |
219 | |
220 } // namespace ash | |
OLD | NEW |