OLD | NEW |
| (Empty) |
1 // Copyright 2016 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/common/system/tray/tray_popup_utils.h" | |
6 | |
7 #include <algorithm> | |
8 #include <utility> | |
9 | |
10 #include "ash/common/ash_constants.h" | |
11 #include "ash/common/ash_view_ids.h" | |
12 #include "ash/common/session/session_state_delegate.h" | |
13 #include "ash/common/system/tray/fixed_sized_image_view.h" | |
14 #include "ash/common/system/tray/size_range_layout.h" | |
15 #include "ash/common/system/tray/tray_constants.h" | |
16 #include "ash/common/system/tray/tray_popup_item_style.h" | |
17 #include "ash/common/wm_shell.h" | |
18 #include "ash/resources/vector_icons/vector_icons.h" | |
19 #include "base/memory/ptr_util.h" | |
20 #include "ui/base/l10n/l10n_util.h" | |
21 #include "ui/gfx/paint_vector_icon.h" | |
22 #include "ui/views/animation/flood_fill_ink_drop_ripple.h" | |
23 #include "ui/views/animation/ink_drop_highlight.h" | |
24 #include "ui/views/animation/ink_drop_impl.h" | |
25 #include "ui/views/animation/ink_drop_mask.h" | |
26 #include "ui/views/animation/square_ink_drop_ripple.h" | |
27 #include "ui/views/background.h" | |
28 #include "ui/views/border.h" | |
29 #include "ui/views/controls/button/button.h" | |
30 #include "ui/views/controls/button/label_button.h" | |
31 #include "ui/views/controls/button/md_text_button.h" | |
32 #include "ui/views/controls/button/toggle_button.h" | |
33 #include "ui/views/controls/image_view.h" | |
34 #include "ui/views/controls/label.h" | |
35 #include "ui/views/controls/separator.h" | |
36 #include "ui/views/controls/slider.h" | |
37 #include "ui/views/layout/box_layout.h" | |
38 #include "ui/views/layout/fill_layout.h" | |
39 #include "ui/views/painter.h" | |
40 | |
41 namespace ash { | |
42 | |
43 namespace { | |
44 | |
45 // Creates a layout manager that positions Views vertically. The Views will be | |
46 // stretched horizontally and centered vertically. | |
47 std::unique_ptr<views::LayoutManager> CreateDefaultCenterLayoutManager() { | |
48 // TODO(bruthig): Use constants instead of magic numbers. | |
49 auto box_layout = | |
50 base::MakeUnique<views::BoxLayout>(views::BoxLayout::kVertical, 4, 8, 0); | |
51 box_layout->set_main_axis_alignment( | |
52 views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER); | |
53 box_layout->set_cross_axis_alignment( | |
54 views::BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH); | |
55 return std::move(box_layout); | |
56 } | |
57 | |
58 // Creates a layout manager that positions Views horizontally. The Views will be | |
59 // centered along the horizontal and vertical axis. | |
60 std::unique_ptr<views::LayoutManager> CreateDefaultEndsLayoutManager() { | |
61 auto box_layout = base::MakeUnique<views::BoxLayout>( | |
62 views::BoxLayout::kHorizontal, 0, 0, 0); | |
63 box_layout->set_main_axis_alignment( | |
64 views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER); | |
65 box_layout->set_cross_axis_alignment( | |
66 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER); | |
67 return std::move(box_layout); | |
68 } | |
69 | |
70 std::unique_ptr<views::LayoutManager> CreateDefaultLayoutManager( | |
71 TriView::Container container) { | |
72 switch (container) { | |
73 case TriView::Container::START: | |
74 case TriView::Container::END: | |
75 return CreateDefaultEndsLayoutManager(); | |
76 case TriView::Container::CENTER: | |
77 return CreateDefaultCenterLayoutManager(); | |
78 } | |
79 // Required by some compilers. | |
80 NOTREACHED(); | |
81 return nullptr; | |
82 } | |
83 | |
84 // Configures the default size and flex value for the specified |container| | |
85 // of the given |tri_view|. Used by CreateDefaultRowView(). | |
86 void ConfigureDefaultSizeAndFlex(TriView* tri_view, | |
87 TriView::Container container) { | |
88 int min_width = 0; | |
89 switch (container) { | |
90 case TriView::Container::START: | |
91 min_width = kTrayPopupItemMinStartWidth; | |
92 break; | |
93 case TriView::Container::CENTER: | |
94 tri_view->SetFlexForContainer(TriView::Container::CENTER, 1.f); | |
95 break; | |
96 case TriView::Container::END: | |
97 min_width = kTrayPopupItemMinEndWidth; | |
98 break; | |
99 } | |
100 | |
101 tri_view->SetMinSize(container, | |
102 gfx::Size(min_width, kTrayPopupItemMinHeight)); | |
103 constexpr int kTrayPopupItemMaxHeight = 144; | |
104 tri_view->SetMaxSize( | |
105 container, | |
106 gfx::Size(SizeRangeLayout::kAbsoluteMaxSize, kTrayPopupItemMaxHeight)); | |
107 } | |
108 | |
109 class BorderlessLabelButton : public views::LabelButton { | |
110 public: | |
111 BorderlessLabelButton(views::ButtonListener* listener, | |
112 const base::string16& text) | |
113 : LabelButton(listener, text) { | |
114 const int kHorizontalPadding = 20; | |
115 SetBorder(views::CreateEmptyBorder(gfx::Insets(0, kHorizontalPadding))); | |
116 TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::BUTTON); | |
117 style.SetupLabel(label()); | |
118 SetHorizontalAlignment(gfx::ALIGN_CENTER); | |
119 SetFocusPainter(TrayPopupUtils::CreateFocusPainter()); | |
120 | |
121 TrayPopupUtils::ConfigureTrayPopupButton(this); | |
122 } | |
123 | |
124 ~BorderlessLabelButton() override {} | |
125 | |
126 // views::LabelButton: | |
127 int GetHeightForWidth(int width) const override { return kMenuButtonSize; } | |
128 | |
129 private: | |
130 // TODO(estade,bruthig): there's a lot in common here with ActionableView. | |
131 // Find a way to share. See related TODO on InkDropHostView::SetInkDropMode(). | |
132 std::unique_ptr<views::InkDrop> CreateInkDrop() override { | |
133 return TrayPopupUtils::CreateInkDrop(TrayPopupInkDropStyle::INSET_BOUNDS, | |
134 this); | |
135 } | |
136 | |
137 std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override { | |
138 return TrayPopupUtils::CreateInkDropRipple( | |
139 TrayPopupInkDropStyle::INSET_BOUNDS, this, | |
140 GetInkDropCenterBasedOnLastEvent()); | |
141 } | |
142 | |
143 std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight() | |
144 const override { | |
145 return TrayPopupUtils::CreateInkDropHighlight( | |
146 TrayPopupInkDropStyle::INSET_BOUNDS, this); | |
147 } | |
148 | |
149 std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override { | |
150 return TrayPopupUtils::CreateInkDropMask( | |
151 TrayPopupInkDropStyle::INSET_BOUNDS, this); | |
152 } | |
153 | |
154 DISALLOW_COPY_AND_ASSIGN(BorderlessLabelButton); | |
155 }; | |
156 | |
157 } // namespace | |
158 | |
159 TriView* TrayPopupUtils::CreateDefaultRowView() { | |
160 TriView* tri_view = CreateMultiTargetRowView(); | |
161 | |
162 tri_view->SetContainerLayout( | |
163 TriView::Container::START, | |
164 CreateDefaultLayoutManager(TriView::Container::START)); | |
165 tri_view->SetContainerLayout( | |
166 TriView::Container::CENTER, | |
167 CreateDefaultLayoutManager(TriView::Container::CENTER)); | |
168 tri_view->SetContainerLayout( | |
169 TriView::Container::END, | |
170 CreateDefaultLayoutManager(TriView::Container::END)); | |
171 | |
172 return tri_view; | |
173 } | |
174 | |
175 TriView* TrayPopupUtils::CreateSubHeaderRowView() { | |
176 TriView* tri_view = CreateMultiTargetRowView(); | |
177 tri_view->SetInsets(gfx::Insets(0, kTrayPopupPaddingHorizontal, 0, 0)); | |
178 tri_view->SetContainerVisible(TriView::Container::START, false); | |
179 tri_view->SetContainerLayout( | |
180 TriView::Container::END, | |
181 CreateDefaultLayoutManager(TriView::Container::END)); | |
182 return tri_view; | |
183 } | |
184 | |
185 TriView* TrayPopupUtils::CreateMultiTargetRowView() { | |
186 TriView* tri_view = new TriView(0 /* padding_between_items */); | |
187 | |
188 tri_view->SetInsets(gfx::Insets(0, kMenuExtraMarginFromLeftEdge, 0, 0)); | |
189 | |
190 ConfigureDefaultSizeAndFlex(tri_view, TriView::Container::START); | |
191 ConfigureDefaultSizeAndFlex(tri_view, TriView::Container::CENTER); | |
192 ConfigureDefaultSizeAndFlex(tri_view, TriView::Container::END); | |
193 | |
194 tri_view->SetContainerLayout(TriView::Container::START, | |
195 base::MakeUnique<views::FillLayout>()); | |
196 tri_view->SetContainerLayout(TriView::Container::CENTER, | |
197 base::MakeUnique<views::FillLayout>()); | |
198 tri_view->SetContainerLayout(TriView::Container::END, | |
199 base::MakeUnique<views::FillLayout>()); | |
200 | |
201 return tri_view; | |
202 } | |
203 | |
204 views::Label* TrayPopupUtils::CreateDefaultLabel() { | |
205 views::Label* label = new views::Label(); | |
206 label->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
207 label->SetBorder( | |
208 views::CreateEmptyBorder(0, 0, 0, kTrayPopupLabelRightPadding)); | |
209 // Frequently the label will paint to a layer that's non-opaque, so subpixel | |
210 // rendering won't work unless we explicitly set a background. See | |
211 // crbug.com/686363 | |
212 label->set_background( | |
213 views::Background::CreateSolidBackground(kBackgroundColor)); | |
214 label->SetBackgroundColor(kBackgroundColor); | |
215 return label; | |
216 } | |
217 | |
218 views::ImageView* TrayPopupUtils::CreateMainImageView() { | |
219 return new FixedSizedImageView(kTrayPopupItemMinStartWidth, | |
220 kTrayPopupItemMinHeight); | |
221 } | |
222 | |
223 views::ImageView* TrayPopupUtils::CreateMoreImageView() { | |
224 views::ImageView* image = | |
225 new FixedSizedImageView(kMenuIconSize, kMenuIconSize); | |
226 image->EnableCanvasFlippingForRTLUI(true); | |
227 image->SetImage( | |
228 gfx::CreateVectorIcon(kSystemMenuArrowRightIcon, kMenuIconColor)); | |
229 return image; | |
230 } | |
231 | |
232 views::Slider* TrayPopupUtils::CreateSlider(views::SliderListener* listener) { | |
233 views::Slider* slider = new views::Slider(listener); | |
234 slider->SetBorder(views::CreateEmptyBorder(gfx::Insets(0, 16))); | |
235 return slider; | |
236 } | |
237 | |
238 views::ToggleButton* TrayPopupUtils::CreateToggleButton( | |
239 views::ButtonListener* listener, | |
240 int accessible_name_id) { | |
241 views::ToggleButton* toggle = new views::ToggleButton(listener); | |
242 const gfx::Size toggle_size(toggle->GetPreferredSize()); | |
243 const int vertical_padding = (kMenuButtonSize - toggle_size.height()) / 2; | |
244 const int horizontal_padding = | |
245 (kTrayToggleButtonWidth - toggle_size.width()) / 2; | |
246 toggle->SetBorder(views::CreateEmptyBorder( | |
247 gfx::Insets(vertical_padding, horizontal_padding))); | |
248 toggle->SetFocusPainter(CreateFocusPainter()); | |
249 toggle->SetAccessibleName(l10n_util::GetStringUTF16(accessible_name_id)); | |
250 return toggle; | |
251 } | |
252 | |
253 std::unique_ptr<views::Painter> TrayPopupUtils::CreateFocusPainter() { | |
254 return views::Painter::CreateSolidFocusPainter( | |
255 kFocusBorderColor, kFocusBorderThickness, gfx::InsetsF()); | |
256 } | |
257 | |
258 void TrayPopupUtils::ConfigureTrayPopupButton(views::CustomButton* button) { | |
259 // All buttons that call into here want this focus painter, but | |
260 // SetFocusPainter is defined separately on derived classes and isn't part of | |
261 // CustomButton. TODO(estade): Address this. | |
262 // button->SetFocusPainter(TrayPopupUtils::CreateFocusPainter()); | |
263 button->SetFocusForPlatform(); | |
264 | |
265 button->SetInkDropMode(views::InkDropHostView::InkDropMode::ON); | |
266 button->set_has_ink_drop_action_on_click(true); | |
267 button->set_ink_drop_base_color(kTrayPopupInkDropBaseColor); | |
268 button->set_ink_drop_visible_opacity(kTrayPopupInkDropRippleOpacity); | |
269 } | |
270 | |
271 void TrayPopupUtils::ConfigureAsStickyHeader(views::View* view) { | |
272 view->set_id(VIEW_ID_STICKY_HEADER); | |
273 view->set_background( | |
274 views::Background::CreateSolidBackground(kBackgroundColor)); | |
275 view->SetBorder( | |
276 views::CreateEmptyBorder(gfx::Insets(kMenuSeparatorVerticalPadding, 0))); | |
277 view->SetPaintToLayer(); | |
278 view->layer()->SetFillsBoundsOpaquely(false); | |
279 } | |
280 | |
281 void TrayPopupUtils::ShowStickyHeaderSeparator(views::View* view, | |
282 bool show_separator) { | |
283 if (show_separator) { | |
284 view->SetBorder(views::CreatePaddedBorder( | |
285 views::CreateSolidSidedBorder(0, 0, kSeparatorWidth, 0, | |
286 kMenuSeparatorColor), | |
287 gfx::Insets(kMenuSeparatorVerticalPadding, 0, | |
288 kMenuSeparatorVerticalPadding - kSeparatorWidth, 0))); | |
289 } else { | |
290 view->SetBorder(views::CreateEmptyBorder( | |
291 gfx::Insets(kMenuSeparatorVerticalPadding, 0))); | |
292 } | |
293 view->SchedulePaint(); | |
294 } | |
295 | |
296 void TrayPopupUtils::ConfigureContainer(TriView::Container container, | |
297 views::View* container_view) { | |
298 container_view->SetLayoutManager( | |
299 CreateDefaultLayoutManager(container).release()); | |
300 } | |
301 | |
302 views::LabelButton* TrayPopupUtils::CreateTrayPopupBorderlessButton( | |
303 views::ButtonListener* listener, | |
304 const base::string16& text) { | |
305 return new BorderlessLabelButton(listener, text); | |
306 } | |
307 | |
308 views::LabelButton* TrayPopupUtils::CreateTrayPopupButton( | |
309 views::ButtonListener* listener, | |
310 const base::string16& text) { | |
311 auto* button = views::MdTextButton::Create(listener, text); | |
312 button->SetProminent(true); | |
313 return button; | |
314 } | |
315 | |
316 views::Separator* TrayPopupUtils::CreateVerticalSeparator() { | |
317 views::Separator* separator = new views::Separator(); | |
318 separator->SetPreferredHeight(24); | |
319 separator->SetColor(kMenuSeparatorColor); | |
320 return separator; | |
321 } | |
322 | |
323 std::unique_ptr<views::InkDrop> TrayPopupUtils::CreateInkDrop( | |
324 TrayPopupInkDropStyle ink_drop_style, | |
325 views::InkDropHostView* host) { | |
326 std::unique_ptr<views::InkDropImpl> ink_drop = | |
327 base::MakeUnique<views::InkDropImpl>(host, host->size()); | |
328 ink_drop->SetAutoHighlightMode( | |
329 views::InkDropImpl::AutoHighlightMode::SHOW_ON_RIPPLE); | |
330 ink_drop->SetShowHighlightOnHover(false); | |
331 | |
332 return std::move(ink_drop); | |
333 } | |
334 | |
335 std::unique_ptr<views::InkDropRipple> TrayPopupUtils::CreateInkDropRipple( | |
336 TrayPopupInkDropStyle ink_drop_style, | |
337 const views::View* host, | |
338 const gfx::Point& center_point, | |
339 SkColor color) { | |
340 return base::MakeUnique<views::FloodFillInkDropRipple>( | |
341 host->size(), TrayPopupUtils::GetInkDropInsets(ink_drop_style), | |
342 center_point, color, kTrayPopupInkDropRippleOpacity); | |
343 } | |
344 | |
345 std::unique_ptr<views::InkDropHighlight> TrayPopupUtils::CreateInkDropHighlight( | |
346 TrayPopupInkDropStyle ink_drop_style, | |
347 const views::View* host, | |
348 SkColor color) { | |
349 const gfx::Rect bounds = | |
350 TrayPopupUtils::GetInkDropBounds(ink_drop_style, host); | |
351 std::unique_ptr<views::InkDropHighlight> highlight( | |
352 new views::InkDropHighlight(bounds.size(), 0, | |
353 gfx::PointF(bounds.CenterPoint()), color)); | |
354 highlight->set_visible_opacity(kTrayPopupInkDropHighlightOpacity); | |
355 return highlight; | |
356 } | |
357 | |
358 std::unique_ptr<views::InkDropMask> TrayPopupUtils::CreateInkDropMask( | |
359 TrayPopupInkDropStyle ink_drop_style, | |
360 const views::View* host) { | |
361 if (ink_drop_style == TrayPopupInkDropStyle::FILL_BOUNDS) | |
362 return nullptr; | |
363 | |
364 const gfx::Size layer_size = host->size(); | |
365 switch (ink_drop_style) { | |
366 case TrayPopupInkDropStyle::HOST_CENTERED: { | |
367 const gfx::Rect mask_bounds = | |
368 GetInkDropBounds(TrayPopupInkDropStyle::HOST_CENTERED, host); | |
369 const int radius = | |
370 std::min(mask_bounds.width(), mask_bounds.height()) / 2; | |
371 return base::MakeUnique<views::CircleInkDropMask>( | |
372 layer_size, mask_bounds.CenterPoint(), radius); | |
373 } | |
374 case TrayPopupInkDropStyle::INSET_BOUNDS: { | |
375 const gfx::Insets mask_insets = | |
376 GetInkDropInsets(TrayPopupInkDropStyle::INSET_BOUNDS); | |
377 return base::MakeUnique<views::RoundRectInkDropMask>( | |
378 layer_size, mask_insets, kTrayPopupInkDropCornerRadius); | |
379 } | |
380 case TrayPopupInkDropStyle::FILL_BOUNDS: | |
381 // Handled by quick return above. | |
382 break; | |
383 } | |
384 // Required by some compilers. | |
385 NOTREACHED(); | |
386 return nullptr; | |
387 } | |
388 | |
389 gfx::Insets TrayPopupUtils::GetInkDropInsets( | |
390 TrayPopupInkDropStyle ink_drop_style) { | |
391 gfx::Insets insets; | |
392 if (ink_drop_style == TrayPopupInkDropStyle::HOST_CENTERED || | |
393 ink_drop_style == TrayPopupInkDropStyle::INSET_BOUNDS) { | |
394 insets.Set(kTrayPopupInkDropInset, kTrayPopupInkDropInset, | |
395 kTrayPopupInkDropInset, kTrayPopupInkDropInset); | |
396 } | |
397 return insets; | |
398 } | |
399 | |
400 gfx::Rect TrayPopupUtils::GetInkDropBounds(TrayPopupInkDropStyle ink_drop_style, | |
401 const views::View* host) { | |
402 gfx::Rect bounds = host->GetLocalBounds(); | |
403 bounds.Inset(GetInkDropInsets(ink_drop_style)); | |
404 return bounds; | |
405 } | |
406 | |
407 views::Separator* TrayPopupUtils::CreateListItemSeparator(bool left_inset) { | |
408 views::Separator* separator = new views::Separator(); | |
409 separator->SetColor(kMenuSeparatorColor); | |
410 separator->SetBorder(views::CreateEmptyBorder( | |
411 kMenuSeparatorVerticalPadding - views::Separator::kThickness, | |
412 left_inset | |
413 ? kMenuExtraMarginFromLeftEdge + kMenuButtonSize + | |
414 kTrayPopupLabelHorizontalPadding | |
415 : 0, | |
416 kMenuSeparatorVerticalPadding, 0)); | |
417 return separator; | |
418 } | |
419 | |
420 views::Separator* TrayPopupUtils::CreateListSubHeaderSeparator() { | |
421 views::Separator* separator = new views::Separator(); | |
422 separator->SetColor(kMenuSeparatorColor); | |
423 separator->SetBorder(views::CreateEmptyBorder( | |
424 kMenuSeparatorVerticalPadding - views::Separator::kThickness, 0, 0, 0)); | |
425 return separator; | |
426 } | |
427 | |
428 bool TrayPopupUtils::CanOpenWebUISettings(LoginStatus status) { | |
429 // TODO(tdanderson): Consider moving this into WmShell, or introduce a | |
430 // CanShowSettings() method in each delegate type that has a | |
431 // ShowSettings() method. | |
432 return status != LoginStatus::NOT_LOGGED_IN && | |
433 status != LoginStatus::LOCKED && | |
434 !WmShell::Get()->GetSessionStateDelegate()->IsInSecondaryLoginScreen(); | |
435 } | |
436 | |
437 } // namespace ash | |
OLD | NEW |