| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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/user/user_card_view.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <memory> | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "ash/common/ash_view_ids.h" | |
| 12 #include "ash/common/login_status.h" | |
| 13 #include "ash/common/media_controller.h" | |
| 14 #include "ash/common/session/session_state_delegate.h" | |
| 15 #include "ash/common/system/tray/system_tray_controller.h" | |
| 16 #include "ash/common/system/tray/system_tray_delegate.h" | |
| 17 #include "ash/common/system/tray/tray_constants.h" | |
| 18 #include "ash/common/system/tray/tray_popup_item_style.h" | |
| 19 #include "ash/common/system/user/rounded_image_view.h" | |
| 20 #include "ash/common/wm_shell.h" | |
| 21 #include "ash/resources/vector_icons/vector_icons.h" | |
| 22 #include "ash/strings/grit/ash_strings.h" | |
| 23 #include "base/i18n/rtl.h" | |
| 24 #include "base/memory/ptr_util.h" | |
| 25 #include "base/strings/string16.h" | |
| 26 #include "base/strings/string_util.h" | |
| 27 #include "base/strings/utf_string_conversions.h" | |
| 28 #include "components/user_manager/user_info.h" | |
| 29 #include "ui/accessibility/ax_node_data.h" | |
| 30 #include "ui/base/l10n/l10n_util.h" | |
| 31 #include "ui/compositor/compositing_recorder.h" | |
| 32 #include "ui/gfx/canvas.h" | |
| 33 #include "ui/gfx/color_palette.h" | |
| 34 #include "ui/gfx/geometry/insets.h" | |
| 35 #include "ui/gfx/geometry/rect.h" | |
| 36 #include "ui/gfx/geometry/size.h" | |
| 37 #include "ui/gfx/paint_vector_icon.h" | |
| 38 #include "ui/gfx/range/range.h" | |
| 39 #include "ui/gfx/render_text.h" | |
| 40 #include "ui/gfx/text_elider.h" | |
| 41 #include "ui/gfx/text_utils.h" | |
| 42 #include "ui/views/border.h" | |
| 43 #include "ui/views/controls/image_view.h" | |
| 44 #include "ui/views/controls/link.h" | |
| 45 #include "ui/views/controls/link_listener.h" | |
| 46 #include "ui/views/layout/box_layout.h" | |
| 47 | |
| 48 namespace ash { | |
| 49 namespace tray { | |
| 50 | |
| 51 namespace { | |
| 52 | |
| 53 const int kUserDetailsVerticalPadding = 5; | |
| 54 | |
| 55 // The invisible word joiner character, used as a marker to indicate the start | |
| 56 // and end of the user's display name in the public account user card's text. | |
| 57 const base::char16 kDisplayNameMark[] = {0x2060, 0}; | |
| 58 | |
| 59 views::View* CreateUserAvatarView(LoginStatus login_status, int user_index) { | |
| 60 RoundedImageView* image_view = new RoundedImageView(kTrayItemSize / 2); | |
| 61 if (login_status == LoginStatus::GUEST) { | |
| 62 gfx::ImageSkia icon = | |
| 63 gfx::CreateVectorIcon(kSystemMenuGuestIcon, kMenuIconColor); | |
| 64 image_view->SetImage(icon, icon.size()); | |
| 65 } else { | |
| 66 SessionStateDelegate* delegate = WmShell::Get()->GetSessionStateDelegate(); | |
| 67 image_view->SetImage(delegate->GetUserInfo(user_index)->GetImage(), | |
| 68 gfx::Size(kTrayItemSize, kTrayItemSize)); | |
| 69 } | |
| 70 | |
| 71 image_view->SetBorder(views::CreateEmptyBorder(gfx::Insets( | |
| 72 (kTrayPopupItemMinStartWidth - image_view->GetPreferredSize().width()) / | |
| 73 2))); | |
| 74 return image_view; | |
| 75 } | |
| 76 | |
| 77 // The user details shown in public account mode. This is essentially a label | |
| 78 // but with custom painting code as the text is styled with multiple colors and | |
| 79 // contains a link. | |
| 80 class PublicAccountUserDetails : public views::View, | |
| 81 public views::LinkListener { | |
| 82 public: | |
| 83 PublicAccountUserDetails(int max_width); | |
| 84 ~PublicAccountUserDetails() override; | |
| 85 | |
| 86 private: | |
| 87 // Overridden from views::View. | |
| 88 void Layout() override; | |
| 89 gfx::Size GetPreferredSize() const override; | |
| 90 void OnPaint(gfx::Canvas* canvas) override; | |
| 91 void GetAccessibleNodeData(ui::AXNodeData* node_data) override; | |
| 92 | |
| 93 // Overridden from views::LinkListener. | |
| 94 void LinkClicked(views::Link* source, int event_flags) override; | |
| 95 | |
| 96 // Calculate a preferred size that ensures the label text and the following | |
| 97 // link do not wrap over more than three lines in total for aesthetic reasons | |
| 98 // if possible. | |
| 99 void CalculatePreferredSize(); | |
| 100 | |
| 101 base::string16 text_; | |
| 102 views::Link* learn_more_; | |
| 103 gfx::Size preferred_size_; | |
| 104 std::vector<std::unique_ptr<gfx::RenderText>> lines_; | |
| 105 | |
| 106 DISALLOW_COPY_AND_ASSIGN(PublicAccountUserDetails); | |
| 107 }; | |
| 108 | |
| 109 PublicAccountUserDetails::PublicAccountUserDetails(int max_width) | |
| 110 : learn_more_(NULL) { | |
| 111 const int inner_padding = | |
| 112 kTrayPopupPaddingHorizontal - kTrayPopupPaddingBetweenItems; | |
| 113 const bool rtl = base::i18n::IsRTL(); | |
| 114 SetBorder(views::CreateEmptyBorder( | |
| 115 kUserDetailsVerticalPadding, rtl ? 0 : inner_padding, | |
| 116 kUserDetailsVerticalPadding, rtl ? inner_padding : 0)); | |
| 117 | |
| 118 // Retrieve the user's display name and wrap it with markers. | |
| 119 // Note that since this is a public account it always has to be the primary | |
| 120 // user. | |
| 121 base::string16 display_name = WmShell::Get() | |
| 122 ->GetSessionStateDelegate() | |
| 123 ->GetUserInfo(0) | |
| 124 ->GetDisplayName(); | |
| 125 base::RemoveChars(display_name, kDisplayNameMark, &display_name); | |
| 126 display_name = kDisplayNameMark[0] + display_name + kDisplayNameMark[0]; | |
| 127 // Retrieve the domain managing the device and wrap it with markers. | |
| 128 base::string16 domain = base::UTF8ToUTF16( | |
| 129 WmShell::Get()->system_tray_delegate()->GetEnterpriseDomain()); | |
| 130 base::RemoveChars(domain, kDisplayNameMark, &domain); | |
| 131 base::i18n::WrapStringWithLTRFormatting(&domain); | |
| 132 // Retrieve the label text, inserting the display name and domain. | |
| 133 text_ = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_PUBLIC_LABEL, | |
| 134 display_name, domain); | |
| 135 | |
| 136 learn_more_ = new views::Link(l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE)); | |
| 137 learn_more_->SetUnderline(false); | |
| 138 learn_more_->set_listener(this); | |
| 139 AddChildView(learn_more_); | |
| 140 | |
| 141 CalculatePreferredSize(); | |
| 142 } | |
| 143 | |
| 144 PublicAccountUserDetails::~PublicAccountUserDetails() {} | |
| 145 | |
| 146 void PublicAccountUserDetails::Layout() { | |
| 147 lines_.clear(); | |
| 148 const gfx::Rect contents_area = GetContentsBounds(); | |
| 149 if (contents_area.IsEmpty()) | |
| 150 return; | |
| 151 | |
| 152 // Word-wrap the label text. | |
| 153 const gfx::FontList font_list; | |
| 154 std::vector<base::string16> lines; | |
| 155 gfx::ElideRectangleText(text_, font_list, contents_area.width(), | |
| 156 contents_area.height(), gfx::ELIDE_LONG_WORDS, | |
| 157 &lines); | |
| 158 // Loop through the lines, creating a renderer for each. | |
| 159 gfx::Point position = contents_area.origin(); | |
| 160 gfx::Range display_name(gfx::Range::InvalidRange()); | |
| 161 for (auto it = lines.begin(); it != lines.end(); ++it) { | |
| 162 auto line = base::WrapUnique(gfx::RenderText::CreateInstance()); | |
| 163 line->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_UI); | |
| 164 line->SetText(*it); | |
| 165 const gfx::Size size(contents_area.width(), line->GetStringSize().height()); | |
| 166 line->SetDisplayRect(gfx::Rect(position, size)); | |
| 167 position.set_y(position.y() + size.height()); | |
| 168 | |
| 169 // Set the default text color for the line. | |
| 170 line->SetColor(kPublicAccountUserCardTextColor); | |
| 171 | |
| 172 // If a range of the line contains the user's display name, apply a custom | |
| 173 // text color to it. | |
| 174 if (display_name.is_empty()) | |
| 175 display_name.set_start(it->find(kDisplayNameMark)); | |
| 176 if (!display_name.is_empty()) { | |
| 177 display_name.set_end( | |
| 178 it->find(kDisplayNameMark, display_name.start() + 1)); | |
| 179 gfx::Range line_range(0, it->size()); | |
| 180 line->ApplyColor(kPublicAccountUserCardNameColor, | |
| 181 display_name.Intersect(line_range)); | |
| 182 // Update the range for the next line. | |
| 183 if (display_name.end() >= line_range.end()) | |
| 184 display_name.set_start(0); | |
| 185 else | |
| 186 display_name = gfx::Range::InvalidRange(); | |
| 187 } | |
| 188 | |
| 189 lines_.push_back(std::move(line)); | |
| 190 } | |
| 191 | |
| 192 // Position the link after the label text, separated by a space. If it does | |
| 193 // not fit onto the last line of the text, wrap the link onto its own line. | |
| 194 const gfx::Size last_line_size = lines_.back()->GetStringSize(); | |
| 195 const int space_width = | |
| 196 gfx::GetStringWidth(base::ASCIIToUTF16(" "), font_list); | |
| 197 const gfx::Size link_size = learn_more_->GetPreferredSize(); | |
| 198 if (contents_area.width() - last_line_size.width() >= | |
| 199 space_width + link_size.width()) { | |
| 200 position.set_x(position.x() + last_line_size.width() + space_width); | |
| 201 position.set_y(position.y() - last_line_size.height()); | |
| 202 } | |
| 203 position.set_y(position.y() - learn_more_->GetInsets().top()); | |
| 204 gfx::Rect learn_more_bounds(position, link_size); | |
| 205 learn_more_bounds.Intersect(contents_area); | |
| 206 if (base::i18n::IsRTL()) { | |
| 207 const gfx::Insets insets = GetInsets(); | |
| 208 learn_more_bounds.Offset(insets.right() - insets.left(), 0); | |
| 209 } | |
| 210 learn_more_->SetBoundsRect(learn_more_bounds); | |
| 211 } | |
| 212 | |
| 213 gfx::Size PublicAccountUserDetails::GetPreferredSize() const { | |
| 214 return preferred_size_; | |
| 215 } | |
| 216 | |
| 217 void PublicAccountUserDetails::OnPaint(gfx::Canvas* canvas) { | |
| 218 for (const auto& line : lines_) | |
| 219 line->Draw(canvas); | |
| 220 | |
| 221 views::View::OnPaint(canvas); | |
| 222 } | |
| 223 | |
| 224 void PublicAccountUserDetails::GetAccessibleNodeData( | |
| 225 ui::AXNodeData* node_data) { | |
| 226 node_data->role = ui::AX_ROLE_STATIC_TEXT; | |
| 227 node_data->SetName(text_); | |
| 228 } | |
| 229 | |
| 230 void PublicAccountUserDetails::LinkClicked(views::Link* source, | |
| 231 int event_flags) { | |
| 232 DCHECK_EQ(source, learn_more_); | |
| 233 WmShell::Get()->system_tray_controller()->ShowPublicAccountInfo(); | |
| 234 } | |
| 235 | |
| 236 void PublicAccountUserDetails::CalculatePreferredSize() { | |
| 237 const gfx::FontList font_list; | |
| 238 const gfx::Size link_size = learn_more_->GetPreferredSize(); | |
| 239 const int space_width = | |
| 240 gfx::GetStringWidth(base::ASCIIToUTF16(" "), font_list); | |
| 241 const gfx::Insets insets = GetInsets(); | |
| 242 int min_width = link_size.width(); | |
| 243 int max_width = | |
| 244 gfx::GetStringWidth(text_, font_list) + space_width + link_size.width(); | |
| 245 | |
| 246 // Do a binary search for the minimum width that ensures no more than three | |
| 247 // lines are needed. The lower bound is the minimum of the current bubble | |
| 248 // width and the width of the link (as no wrapping is permitted inside the | |
| 249 // link). The upper bound is the maximum of the largest allowed bubble width | |
| 250 // and the sum of the label text and link widths when put on a single line. | |
| 251 std::vector<base::string16> lines; | |
| 252 while (min_width < max_width) { | |
| 253 lines.clear(); | |
| 254 const int width = (min_width + max_width) / 2; | |
| 255 const bool too_narrow = | |
| 256 gfx::ElideRectangleText(text_, font_list, width, INT_MAX, | |
| 257 gfx::TRUNCATE_LONG_WORDS, &lines) != 0; | |
| 258 int line_count = lines.size(); | |
| 259 if (!too_narrow && line_count == 3 && | |
| 260 width - gfx::GetStringWidth(lines.back(), font_list) <= | |
| 261 space_width + link_size.width()) | |
| 262 ++line_count; | |
| 263 if (too_narrow || line_count > 3) | |
| 264 min_width = width + 1; | |
| 265 else | |
| 266 max_width = width; | |
| 267 } | |
| 268 | |
| 269 // Calculate the corresponding height and set the preferred size. | |
| 270 lines.clear(); | |
| 271 gfx::ElideRectangleText(text_, font_list, min_width, INT_MAX, | |
| 272 gfx::TRUNCATE_LONG_WORDS, &lines); | |
| 273 int line_count = lines.size(); | |
| 274 if (min_width - gfx::GetStringWidth(lines.back(), font_list) <= | |
| 275 space_width + link_size.width()) { | |
| 276 ++line_count; | |
| 277 } | |
| 278 const int line_height = font_list.GetHeight(); | |
| 279 const int link_extra_height = std::max( | |
| 280 link_size.height() - learn_more_->GetInsets().top() - line_height, 0); | |
| 281 preferred_size_ = | |
| 282 gfx::Size(min_width + insets.width(), | |
| 283 line_count * line_height + link_extra_height + insets.height()); | |
| 284 } | |
| 285 | |
| 286 } // namespace | |
| 287 | |
| 288 UserCardView::UserCardView(LoginStatus login_status, | |
| 289 int max_width, | |
| 290 int user_index) | |
| 291 : user_index_(user_index), | |
| 292 user_name_(nullptr), | |
| 293 media_capture_label_(nullptr), | |
| 294 media_capture_icon_(nullptr) { | |
| 295 auto* layout = new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, | |
| 296 kTrayPopupLabelHorizontalPadding); | |
| 297 SetLayoutManager(layout); | |
| 298 layout->set_minimum_cross_axis_size(kTrayPopupItemMinHeight); | |
| 299 layout->set_cross_axis_alignment( | |
| 300 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER); | |
| 301 // For active users, the left inset is provided by ActiveUserBorder, which | |
| 302 // is necessary to make sure the ripple does not cover that part of the row. | |
| 303 // For inactive users, we set the inset here and this causes the ripple to | |
| 304 // extend all the way to the edges of the menu. | |
| 305 if (!is_active_user()) | |
| 306 SetBorder(views::CreateEmptyBorder(0, kMenuExtraMarginFromLeftEdge, 0, 0)); | |
| 307 | |
| 308 WmShell::Get()->media_controller()->AddObserver(this); | |
| 309 | |
| 310 if (login_status == LoginStatus::PUBLIC) | |
| 311 AddPublicModeUserContent(max_width); | |
| 312 else | |
| 313 AddUserContent(layout, login_status); | |
| 314 } | |
| 315 | |
| 316 UserCardView::~UserCardView() { | |
| 317 WmShell::Get()->media_controller()->RemoveObserver(this); | |
| 318 } | |
| 319 | |
| 320 void UserCardView::PaintChildren(const ui::PaintContext& context) { | |
| 321 if (!is_active_user()) { | |
| 322 ui::CompositingRecorder alpha(context, 0xFF / 2, true); | |
| 323 View::PaintChildren(context); | |
| 324 } else { | |
| 325 View::PaintChildren(context); | |
| 326 } | |
| 327 } | |
| 328 | |
| 329 void UserCardView::GetAccessibleNodeData(ui::AXNodeData* node_data) { | |
| 330 node_data->role = ui::AX_ROLE_STATIC_TEXT; | |
| 331 std::vector<base::string16> labels; | |
| 332 | |
| 333 // Construct the name by concatenating descendants' names. | |
| 334 std::list<views::View*> descendants; | |
| 335 descendants.push_back(this); | |
| 336 while (!descendants.empty()) { | |
| 337 auto* view = descendants.front(); | |
| 338 descendants.pop_front(); | |
| 339 if (view != this) { | |
| 340 ui::AXNodeData descendant_data; | |
| 341 view->GetAccessibleNodeData(&descendant_data); | |
| 342 base::string16 label = | |
| 343 descendant_data.GetString16Attribute(ui::AX_ATTR_NAME); | |
| 344 // If we find a non-empty name, use that and don't descend further into | |
| 345 // the tree. | |
| 346 if (!label.empty()) { | |
| 347 labels.push_back(label); | |
| 348 continue; | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 // This view didn't have its own name, so look over its children. | |
| 353 for (int i = view->child_count() - 1; i >= 0; --i) | |
| 354 descendants.push_front(view->child_at(i)); | |
| 355 } | |
| 356 node_data->SetName(base::JoinString(labels, base::ASCIIToUTF16(" "))); | |
| 357 } | |
| 358 | |
| 359 void UserCardView::OnMediaCaptureChanged( | |
| 360 const std::vector<mojom::MediaCaptureState>& capture_states) { | |
| 361 if (is_active_user()) | |
| 362 return; | |
| 363 | |
| 364 mojom::MediaCaptureState state = capture_states[user_index_]; | |
| 365 int res_id = 0; | |
| 366 switch (state) { | |
| 367 case mojom::MediaCaptureState::AUDIO_VIDEO: | |
| 368 res_id = IDS_ASH_STATUS_TRAY_MEDIA_RECORDING_AUDIO_VIDEO; | |
| 369 break; | |
| 370 case mojom::MediaCaptureState::AUDIO: | |
| 371 res_id = IDS_ASH_STATUS_TRAY_MEDIA_RECORDING_AUDIO; | |
| 372 break; | |
| 373 case mojom::MediaCaptureState::VIDEO: | |
| 374 res_id = IDS_ASH_STATUS_TRAY_MEDIA_RECORDING_VIDEO; | |
| 375 break; | |
| 376 case mojom::MediaCaptureState::NONE: | |
| 377 break; | |
| 378 } | |
| 379 if (res_id) | |
| 380 media_capture_label_->SetText(l10n_util::GetStringUTF16(res_id)); | |
| 381 media_capture_label_->SetVisible(!!res_id); | |
| 382 media_capture_icon_->SetVisible(!!res_id); | |
| 383 user_name_->SetVisible(!res_id); | |
| 384 Layout(); | |
| 385 } | |
| 386 | |
| 387 void UserCardView::AddPublicModeUserContent(int max_width) { | |
| 388 views::View* avatar = CreateUserAvatarView(LoginStatus::PUBLIC, 0); | |
| 389 AddChildView(avatar); | |
| 390 int details_max_width = max_width - avatar->GetPreferredSize().width() - | |
| 391 kTrayPopupPaddingBetweenItems; | |
| 392 AddChildView(new PublicAccountUserDetails(details_max_width)); | |
| 393 } | |
| 394 | |
| 395 void UserCardView::AddUserContent(views::BoxLayout* layout, | |
| 396 LoginStatus login_status) { | |
| 397 AddChildView(CreateUserAvatarView(login_status, user_index_)); | |
| 398 SessionStateDelegate* delegate = WmShell::Get()->GetSessionStateDelegate(); | |
| 399 base::string16 user_name_string = | |
| 400 login_status == LoginStatus::GUEST | |
| 401 ? l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_GUEST_LABEL) | |
| 402 : delegate->GetUserInfo(user_index_)->GetDisplayName(); | |
| 403 user_name_ = new views::Label(user_name_string); | |
| 404 user_name_->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
| 405 TrayPopupItemStyle user_name_style( | |
| 406 TrayPopupItemStyle::FontStyle::DEFAULT_VIEW_LABEL); | |
| 407 user_name_style.SetupLabel(user_name_); | |
| 408 | |
| 409 TrayPopupItemStyle user_email_style(TrayPopupItemStyle::FontStyle::CAPTION); | |
| 410 // Only the active user's email label is lightened (for the inactive user, the | |
| 411 // label starts as black and the entire row is 54% opacity). | |
| 412 if (is_active_user()) | |
| 413 user_email_style.set_color_style(TrayPopupItemStyle::ColorStyle::INACTIVE); | |
| 414 auto* user_email = new views::Label(); | |
| 415 base::string16 user_email_string; | |
| 416 if (login_status != LoginStatus::GUEST) { | |
| 417 user_email_string = | |
| 418 WmShell::Get()->system_tray_delegate()->IsUserSupervised() | |
| 419 ? l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SUPERVISED_LABEL) | |
| 420 : base::UTF8ToUTF16( | |
| 421 delegate->GetUserInfo(user_index_)->GetDisplayEmail()); | |
| 422 } | |
| 423 user_email->SetText(user_email_string); | |
| 424 user_email->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
| 425 user_email_style.SetupLabel(user_email); | |
| 426 user_email->SetVisible(!user_email_string.empty()); | |
| 427 user_email->set_collapse_when_hidden(true); | |
| 428 | |
| 429 views::View* stack_of_labels = new views::View; | |
| 430 AddChildView(stack_of_labels); | |
| 431 layout->SetFlexForView(stack_of_labels, 1); | |
| 432 stack_of_labels->SetLayoutManager( | |
| 433 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); | |
| 434 stack_of_labels->AddChildView(user_name_); | |
| 435 stack_of_labels->AddChildView(user_email); | |
| 436 // The name and email have different font sizes. This border is designed | |
| 437 // to make both views take up equal space so the whitespace between them | |
| 438 // is centered on the vertical midpoint. | |
| 439 int user_email_bottom_pad = user_name_->GetPreferredSize().height() - | |
| 440 user_email->GetPreferredSize().height(); | |
| 441 user_email->SetBorder( | |
| 442 views::CreateEmptyBorder(0, 0, user_email_bottom_pad, 0)); | |
| 443 | |
| 444 // Only inactive users need media capture indicators. | |
| 445 if (!is_active_user()) { | |
| 446 media_capture_label_ = new views::Label(); | |
| 447 media_capture_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
| 448 media_capture_label_->SetBorder( | |
| 449 views::CreateEmptyBorder(0, 0, user_email_bottom_pad, 0)); | |
| 450 user_email_style.SetupLabel(media_capture_label_); | |
| 451 stack_of_labels->AddChildView(media_capture_label_); | |
| 452 | |
| 453 media_capture_icon_ = new views::ImageView; | |
| 454 media_capture_icon_->SetImage( | |
| 455 gfx::CreateVectorIcon(kSystemTrayRecordingIcon, gfx::kGoogleRed700)); | |
| 456 const int media_capture_width = kTrayPopupItemMinEndWidth; | |
| 457 media_capture_icon_->SetBorder(views::CreateEmptyBorder( | |
| 458 gfx::Insets(0, (media_capture_width - | |
| 459 media_capture_icon_->GetPreferredSize().width()) / | |
| 460 2))); | |
| 461 | |
| 462 media_capture_icon_->set_id(VIEW_ID_USER_VIEW_MEDIA_INDICATOR); | |
| 463 AddChildView(media_capture_icon_); | |
| 464 | |
| 465 WmShell::Get()->media_controller()->RequestCaptureState(); | |
| 466 } | |
| 467 } | |
| 468 | |
| 469 } // namespace tray | |
| 470 } // namespace ash | |
| OLD | NEW |