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/system/tray/tray_popup_utils.h"
6
7 #include <algorithm>
8 #include <memory>
9 #include <utility>
10
11 #include "ash/public/cpp/ash_constants.h"
12 #include "ash/public/cpp/ash_view_ids.h"
13 #include "ash/resources/vector_icons/vector_icons.h"
14 #include "ash/session/session_controller_impl.h"
15 #include "ash/shell.h"
16 #include "ash/style/ash_color_provider.h"
17 #include "ash/system/tray/hover_highlight_view.h"
18 #include "ash/system/tray/size_range_layout.h"
19 #include "ash/system/tray/tray_constants.h"
20 #include "ash/system/tray/unfocusable_label.h"
21 #include "chromeos/ui/vector_icons/vector_icons.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/gfx/color_palette.h"
24 #include "ui/gfx/geometry/insets.h"
25 #include "ui/gfx/paint_vector_icon.h"
26 #include "ui/views/animation/flood_fill_ink_drop_ripple.h"
27 #include "ui/views/animation/ink_drop_highlight.h"
28 #include "ui/views/animation/ink_drop_impl.h"
29 #include "ui/views/animation/square_ink_drop_ripple.h"
30 #include "ui/views/controls/button/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/highlight_path_generator.h"
34 #include "ui/views/controls/image_view.h"
35 #include "ui/views/controls/label.h"
36 #include "ui/views/controls/separator.h"
37 #include "ui/views/controls/slider.h"
38 #include "ui/views/layout/box_layout.h"
39 #include "ui/views/layout/fill_layout.h"
40 #include "ui/views/painter.h"
41
42 namespace ash {
43
44 namespace {
45
46 // Creates a layout manager that positions Views vertically. The Views will be
47 // stretched horizontally and centered vertically.
CreateDefaultCenterLayoutManager()48 std::unique_ptr<views::LayoutManager> CreateDefaultCenterLayoutManager() {
49 // TODO(bruthig): Use constants instead of magic numbers.
50 auto box_layout = std::make_unique<views::BoxLayout>(
51 views::BoxLayout::Orientation::kVertical,
52 gfx::Insets(8, kTrayPopupLabelHorizontalPadding));
53 box_layout->set_main_axis_alignment(
54 views::BoxLayout::MainAxisAlignment::kCenter);
55 box_layout->set_cross_axis_alignment(
56 views::BoxLayout::CrossAxisAlignment::kStretch);
57 return std::move(box_layout);
58 }
59
60 // Creates a layout manager that positions Views horizontally. The Views will be
61 // centered along the horizontal and vertical axis.
CreateDefaultEndsLayoutManager()62 std::unique_ptr<views::LayoutManager> CreateDefaultEndsLayoutManager() {
63 auto box_layout = std::make_unique<views::BoxLayout>(
64 views::BoxLayout::Orientation::kHorizontal);
65 box_layout->set_main_axis_alignment(
66 views::BoxLayout::MainAxisAlignment::kCenter);
67 box_layout->set_cross_axis_alignment(
68 views::BoxLayout::CrossAxisAlignment::kCenter);
69 return std::move(box_layout);
70 }
71
CreateDefaultLayoutManager(TriView::Container container)72 std::unique_ptr<views::LayoutManager> CreateDefaultLayoutManager(
73 TriView::Container container) {
74 switch (container) {
75 case TriView::Container::START:
76 case TriView::Container::END:
77 return CreateDefaultEndsLayoutManager();
78 case TriView::Container::CENTER:
79 return CreateDefaultCenterLayoutManager();
80 }
81 // Required by some compilers.
82 NOTREACHED();
83 return nullptr;
84 }
85
86 // Configures the default size and flex value for the specified |container|
87 // of the given |tri_view|. Used by CreateDefaultRowView().
ConfigureDefaultSizeAndFlex(TriView * tri_view,TriView::Container container)88 void ConfigureDefaultSizeAndFlex(TriView* tri_view,
89 TriView::Container container) {
90 int min_width = 0;
91 switch (container) {
92 case TriView::Container::START:
93 min_width = kTrayPopupItemMinStartWidth;
94 break;
95 case TriView::Container::CENTER:
96 tri_view->SetFlexForContainer(TriView::Container::CENTER, 1.f);
97 break;
98 case TriView::Container::END:
99 min_width = kTrayPopupItemMinEndWidth;
100 break;
101 }
102
103 tri_view->SetMinSize(container,
104 gfx::Size(min_width, kTrayPopupItemMinHeight));
105 constexpr int kTrayPopupItemMaxHeight = 144;
106 tri_view->SetMaxSize(container, gfx::Size(SizeRangeLayout::kAbsoluteMaxSize,
107 kTrayPopupItemMaxHeight));
108 }
109
GetInkDropInsets(TrayPopupInkDropStyle ink_drop_style)110 gfx::Insets GetInkDropInsets(TrayPopupInkDropStyle ink_drop_style) {
111 if (ink_drop_style == TrayPopupInkDropStyle::HOST_CENTERED ||
112 ink_drop_style == TrayPopupInkDropStyle::INSET_BOUNDS) {
113 return gfx::Insets(kTrayPopupInkDropInset);
114 }
115 return gfx::Insets();
116 }
117
118 class HighlightPathGenerator : public views::HighlightPathGenerator {
119 public:
HighlightPathGenerator(TrayPopupInkDropStyle ink_drop_style)120 explicit HighlightPathGenerator(TrayPopupInkDropStyle ink_drop_style)
121 : ink_drop_style_(ink_drop_style) {}
122
123 HighlightPathGenerator(const HighlightPathGenerator&) = delete;
124 HighlightPathGenerator& operator=(const HighlightPathGenerator&) = delete;
125
126 // views::HighlightPathGenerator:
GetRoundRect(const gfx::RectF & rect)127 base::Optional<gfx::RRectF> GetRoundRect(const gfx::RectF& rect) override {
128 gfx::RectF bounds = rect;
129 bounds.Inset(GetInkDropInsets(ink_drop_style_));
130 float corner_radius = 0.f;
131 switch (ink_drop_style_) {
132 case TrayPopupInkDropStyle::HOST_CENTERED:
133 corner_radius = std::min(bounds.width(), bounds.height()) / 2.f;
134 bounds.ClampToCenteredSize(gfx::SizeF(corner_radius, corner_radius));
135 break;
136 case TrayPopupInkDropStyle::INSET_BOUNDS:
137 corner_radius = kTrayPopupInkDropCornerRadius;
138 break;
139 case TrayPopupInkDropStyle::FILL_BOUNDS:
140 break;
141 }
142
143 return gfx::RRectF(bounds, corner_radius);
144 }
145
146 private:
147 const TrayPopupInkDropStyle ink_drop_style_;
148 };
149
150 } // namespace
151
CreateDefaultRowView()152 TriView* TrayPopupUtils::CreateDefaultRowView() {
153 TriView* tri_view = CreateMultiTargetRowView();
154
155 tri_view->SetContainerLayout(
156 TriView::Container::START,
157 CreateDefaultLayoutManager(TriView::Container::START));
158 tri_view->SetContainerLayout(
159 TriView::Container::CENTER,
160 CreateDefaultLayoutManager(TriView::Container::CENTER));
161 tri_view->SetContainerLayout(
162 TriView::Container::END,
163 CreateDefaultLayoutManager(TriView::Container::END));
164
165 return tri_view;
166 }
167
CreateSubHeaderRowView(bool start_visible)168 TriView* TrayPopupUtils::CreateSubHeaderRowView(bool start_visible) {
169 TriView* tri_view = CreateDefaultRowView();
170 if (!start_visible) {
171 tri_view->SetInsets(gfx::Insets(
172 0, kTrayPopupPaddingHorizontal - kTrayPopupLabelHorizontalPadding, 0,
173 0));
174 tri_view->SetContainerVisible(TriView::Container::START, false);
175 }
176 return tri_view;
177 }
178
CreateMultiTargetRowView()179 TriView* TrayPopupUtils::CreateMultiTargetRowView() {
180 TriView* tri_view = new TriView(0 /* padding_between_items */);
181
182 tri_view->SetInsets(gfx::Insets(0, kMenuExtraMarginFromLeftEdge, 0, 0));
183
184 ConfigureDefaultSizeAndFlex(tri_view, TriView::Container::START);
185 ConfigureDefaultSizeAndFlex(tri_view, TriView::Container::CENTER);
186 ConfigureDefaultSizeAndFlex(tri_view, TriView::Container::END);
187
188 tri_view->SetContainerLayout(TriView::Container::START,
189 std::make_unique<views::FillLayout>());
190 tri_view->SetContainerLayout(TriView::Container::CENTER,
191 std::make_unique<views::FillLayout>());
192 tri_view->SetContainerLayout(TriView::Container::END,
193 std::make_unique<views::FillLayout>());
194
195 return tri_view;
196 }
197
CreateDefaultLabel()198 views::Label* TrayPopupUtils::CreateDefaultLabel() {
199 views::Label* label = new views::Label();
200 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
201 label->SetSubpixelRenderingEnabled(false);
202 return label;
203 }
204
CreateUnfocusableLabel()205 UnfocusableLabel* TrayPopupUtils::CreateUnfocusableLabel() {
206 UnfocusableLabel* label = new UnfocusableLabel();
207 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
208 label->SetSubpixelRenderingEnabled(false);
209 return label;
210 }
211
CreateMainImageView()212 views::ImageView* TrayPopupUtils::CreateMainImageView() {
213 auto* image = new views::ImageView;
214 image->SetPreferredSize(
215 gfx::Size(kTrayPopupItemMinStartWidth, kTrayPopupItemMinHeight));
216 return image;
217 }
218
CreateToggleButton(views::Button::PressedCallback callback,int accessible_name_id)219 views::ToggleButton* TrayPopupUtils::CreateToggleButton(
220 views::Button::PressedCallback callback,
221 int accessible_name_id) {
222 constexpr SkColor kTrackAlpha = 0x66;
223 auto GetColor = [](bool is_on, SkAlpha alpha = SK_AlphaOPAQUE) {
224 AshColorProvider::ContentLayerType type =
225 is_on ? AshColorProvider::ContentLayerType::kIconColorProminent
226 : AshColorProvider::ContentLayerType::kTextColorPrimary;
227
228 return SkColorSetA(AshColorProvider::Get()->GetContentLayerColor(type),
229 alpha);
230 };
231 views::ToggleButton* toggle = new views::ToggleButton(std::move(callback));
232 const gfx::Size toggle_size(toggle->GetPreferredSize());
233 const int vertical_padding = (kMenuButtonSize - toggle_size.height()) / 2;
234 const int horizontal_padding =
235 (kTrayToggleButtonWidth - toggle_size.width()) / 2;
236 toggle->SetBorder(views::CreateEmptyBorder(
237 gfx::Insets(vertical_padding, horizontal_padding)));
238 toggle->SetAccessibleName(l10n_util::GetStringUTF16(accessible_name_id));
239 toggle->SetThumbOnColor(GetColor(true));
240 toggle->SetThumbOffColor(GetColor(false));
241 toggle->SetTrackOnColor(GetColor(true, kTrackAlpha));
242 toggle->SetTrackOffColor(GetColor(false, kTrackAlpha));
243 return toggle;
244 }
245
CreateFocusPainter()246 std::unique_ptr<views::Painter> TrayPopupUtils::CreateFocusPainter() {
247 return views::Painter::CreateSolidFocusPainter(
248 AshColorProvider::Get()->GetControlsLayerColor(
249 AshColorProvider::ControlsLayerType::kFocusRingColor),
250 kFocusBorderThickness, gfx::InsetsF());
251 }
252
ConfigureTrayPopupButton(views::Button * button)253 void TrayPopupUtils::ConfigureTrayPopupButton(views::Button* button) {
254 button->SetInstallFocusRingOnFocus(true);
255 button->SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
256 button->SetHasInkDropActionOnClick(true);
257 }
258
ConfigureAsStickyHeader(views::View * view)259 void TrayPopupUtils::ConfigureAsStickyHeader(views::View* view) {
260 view->SetID(VIEW_ID_STICKY_HEADER);
261 view->SetBorder(
262 views::CreateEmptyBorder(gfx::Insets(kMenuSeparatorVerticalPadding, 0)));
263 view->SetPaintToLayer();
264 view->layer()->SetFillsBoundsOpaquely(false);
265 }
266
ConfigureContainer(TriView::Container container,views::View * container_view)267 void TrayPopupUtils::ConfigureContainer(TriView::Container container,
268 views::View* container_view) {
269 container_view->SetLayoutManager(CreateDefaultLayoutManager(container));
270 }
271
CreateTrayPopupButton(views::Button::PressedCallback callback,const base::string16 & text)272 views::LabelButton* TrayPopupUtils::CreateTrayPopupButton(
273 views::Button::PressedCallback callback,
274 const base::string16& text) {
275 auto button =
276 std::make_unique<views::MdTextButton>(std::move(callback), text);
277 button->SetProminent(true);
278 return button.release();
279 }
280
CreateVerticalSeparator()281 views::Separator* TrayPopupUtils::CreateVerticalSeparator() {
282 views::Separator* separator = new views::Separator();
283 separator->SetPreferredHeight(24);
284 separator->SetColor(AshColorProvider::Get()->GetContentLayerColor(
285 AshColorProvider::ContentLayerType::kSeparatorColor));
286 return separator;
287 }
288
CreateInkDrop(views::InkDropHostView * host)289 std::unique_ptr<views::InkDrop> TrayPopupUtils::CreateInkDrop(
290 views::InkDropHostView* host) {
291 std::unique_ptr<views::InkDropImpl> ink_drop =
292 std::make_unique<views::InkDropImpl>(host, host->size());
293 ink_drop->SetAutoHighlightMode(
294 views::InkDropImpl::AutoHighlightMode::SHOW_ON_RIPPLE);
295 ink_drop->SetShowHighlightOnHover(false);
296 return std::move(ink_drop);
297 }
298
CreateInkDropRipple(TrayPopupInkDropStyle ink_drop_style,const views::View * host,const gfx::Point & center_point)299 std::unique_ptr<views::InkDropRipple> TrayPopupUtils::CreateInkDropRipple(
300 TrayPopupInkDropStyle ink_drop_style,
301 const views::View* host,
302 const gfx::Point& center_point) {
303 const AshColorProvider::RippleAttributes ripple_attributes =
304 AshColorProvider::Get()->GetRippleAttributes();
305 return std::make_unique<views::FloodFillInkDropRipple>(
306 host->size(), GetInkDropInsets(ink_drop_style), center_point,
307 ripple_attributes.base_color, ripple_attributes.inkdrop_opacity);
308 }
309
CreateInkDropHighlight(const views::View * host)310 std::unique_ptr<views::InkDropHighlight> TrayPopupUtils::CreateInkDropHighlight(
311 const views::View* host) {
312 const AshColorProvider::RippleAttributes ripple_attributes =
313 AshColorProvider::Get()->GetRippleAttributes();
314 auto highlight = std::make_unique<views::InkDropHighlight>(
315 gfx::SizeF(host->size()), ripple_attributes.base_color);
316 highlight->set_visible_opacity(ripple_attributes.highlight_opacity);
317 return highlight;
318 }
319
InstallHighlightPathGenerator(views::View * host,TrayPopupInkDropStyle ink_drop_style)320 void TrayPopupUtils::InstallHighlightPathGenerator(
321 views::View* host,
322 TrayPopupInkDropStyle ink_drop_style) {
323 views::HighlightPathGenerator::Install(
324 host, std::make_unique<HighlightPathGenerator>(ink_drop_style));
325 }
326
CreateListItemSeparator(bool left_inset)327 views::Separator* TrayPopupUtils::CreateListItemSeparator(bool left_inset) {
328 views::Separator* separator = new views::Separator();
329 separator->SetColor(AshColorProvider::Get()->GetContentLayerColor(
330 AshColorProvider::ContentLayerType::kSeparatorColor));
331 separator->SetBorder(views::CreateEmptyBorder(
332 kMenuSeparatorVerticalPadding - views::Separator::kThickness,
333 left_inset ? kMenuExtraMarginFromLeftEdge + kMenuButtonSize +
334 kTrayPopupLabelHorizontalPadding
335 : 0,
336 kMenuSeparatorVerticalPadding, 0));
337 return separator;
338 }
339
CanOpenWebUISettings()340 bool TrayPopupUtils::CanOpenWebUISettings() {
341 return Shell::Get()->session_controller()->ShouldEnableSettings();
342 }
343
InitializeAsCheckableRow(HoverHighlightView * container,bool checked,bool enterprise_managed)344 void TrayPopupUtils::InitializeAsCheckableRow(HoverHighlightView* container,
345 bool checked,
346 bool enterprise_managed) {
347 const int dip_size = GetDefaultSizeOfVectorIcon(kCheckCircleIcon);
348 gfx::ImageSkia check_mark = CreateVectorIcon(
349 kHollowCheckCircleIcon, dip_size,
350 AshColorProvider::Get()->GetContentLayerColor(
351 AshColorProvider::ContentLayerType::kIconColorProminent));
352 if (enterprise_managed) {
353 gfx::ImageSkia enterprise_managed_icon = CreateVectorIcon(
354 chromeos::kEnterpriseIcon, dip_size, gfx::kGoogleGrey100);
355 container->AddRightIcon(enterprise_managed_icon,
356 enterprise_managed_icon.width());
357 }
358 container->AddRightIcon(check_mark, check_mark.width());
359 UpdateCheckMarkVisibility(container, checked);
360 }
361
UpdateCheckMarkVisibility(HoverHighlightView * container,bool visible)362 void TrayPopupUtils::UpdateCheckMarkVisibility(HoverHighlightView* container,
363 bool visible) {
364 container->SetRightViewVisible(visible);
365 container->SetAccessibilityState(
366 visible ? HoverHighlightView::AccessibilityState::CHECKED_CHECKBOX
367 : HoverHighlightView::AccessibilityState::UNCHECKED_CHECKBOX);
368 }
369
SetLabelFontList(views::Label * label,FontStyle style)370 void TrayPopupUtils::SetLabelFontList(views::Label* label, FontStyle style) {
371 label->SetAutoColorReadabilityEnabled(false);
372 const gfx::FontList& base_font_list = views::Label::GetDefaultFontList();
373 switch (style) {
374 case FontStyle::kTitle:
375 label->SetFontList(base_font_list.Derive(8, gfx::Font::NORMAL,
376 gfx::Font::Weight::MEDIUM));
377 break;
378 case FontStyle::kSubHeader:
379 label->SetFontList(base_font_list.Derive(4, gfx::Font::NORMAL,
380 gfx::Font::Weight::MEDIUM));
381 break;
382 case FontStyle::kSmallTitle:
383 case FontStyle::kDetailedViewLabel:
384 case FontStyle::kSystemInfo:
385 label->SetFontList(base_font_list.Derive(1, gfx::Font::NORMAL,
386 gfx::Font::Weight::NORMAL));
387 break;
388 }
389 }
390
391 } // namespace ash
392