| 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/chromeos/palette/palette_tray.h" | |
| 6 | |
| 7 #include "ash/common/material_design/material_design_controller.h" | |
| 8 #include "ash/common/session/session_state_delegate.h" | |
| 9 #include "ash/common/shelf/shelf_constants.h" | |
| 10 #include "ash/common/shelf/wm_shelf.h" | |
| 11 #include "ash/common/shelf/wm_shelf_util.h" | |
| 12 #include "ash/common/system/chromeos/palette/palette_tool_manager.h" | |
| 13 #include "ash/common/system/chromeos/palette/palette_utils.h" | |
| 14 #include "ash/common/system/tray/system_menu_button.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_bubble_wrapper.h" | |
| 18 #include "ash/common/system/tray/tray_constants.h" | |
| 19 #include "ash/common/system/tray/tray_popup_header_button.h" | |
| 20 #include "ash/common/system/tray/tray_popup_item_style.h" | |
| 21 #include "ash/common/wm_shell.h" | |
| 22 #include "ash/common/wm_window.h" | |
| 23 #include "ash/public/cpp/shell_window_ids.h" | |
| 24 #include "ash/resources/grit/ash_resources.h" | |
| 25 #include "ash/resources/vector_icons/vector_icons.h" | |
| 26 #include "ash/root_window_controller.h" | |
| 27 #include "ash/strings/grit/ash_strings.h" | |
| 28 #include "base/metrics/histogram_macros.h" | |
| 29 #include "ui/base/l10n/l10n_util.h" | |
| 30 #include "ui/base/resource/resource_bundle.h" | |
| 31 #include "ui/events/devices/input_device_manager.h" | |
| 32 #include "ui/events/devices/stylus_state.h" | |
| 33 #include "ui/gfx/color_palette.h" | |
| 34 #include "ui/gfx/paint_vector_icon.h" | |
| 35 #include "ui/gfx/vector_icons_public.h" | |
| 36 #include "ui/views/controls/image_view.h" | |
| 37 #include "ui/views/controls/label.h" | |
| 38 #include "ui/views/controls/separator.h" | |
| 39 #include "ui/views/layout/box_layout.h" | |
| 40 #include "ui/views/layout/fill_layout.h" | |
| 41 | |
| 42 namespace ash { | |
| 43 | |
| 44 namespace { | |
| 45 | |
| 46 // Padding for tray icon (dp; the button that shows the palette menu). | |
| 47 constexpr int kTrayIconMainAxisInset = 8; | |
| 48 constexpr int kTrayIconCrossAxisInset = 0; | |
| 49 | |
| 50 // Width of the palette itself (dp). | |
| 51 constexpr int kPaletteWidth = 332; | |
| 52 | |
| 53 // Padding at the top/bottom of the palette (dp). | |
| 54 constexpr int kPalettePaddingOnTop = 4; | |
| 55 constexpr int kPalettePaddingOnBottom = 2; | |
| 56 | |
| 57 // Margins between the title view and the edges around it (dp). | |
| 58 constexpr int kPaddingBetweenTitleAndLeftEdge = 12; | |
| 59 constexpr int kPaddingBetweenTitleAndSeparator = 3; | |
| 60 | |
| 61 // Color of the separator. | |
| 62 const SkColor kPaletteSeparatorColor = SkColorSetARGB(0x1E, 0x00, 0x00, 0x00); | |
| 63 | |
| 64 // Returns true if we are in a user session that can show the stylus tools. | |
| 65 bool IsInUserSession() { | |
| 66 SessionStateDelegate* session_state_delegate = | |
| 67 WmShell::Get()->GetSessionStateDelegate(); | |
| 68 return !session_state_delegate->IsUserSessionBlocked() && | |
| 69 session_state_delegate->GetSessionState() == | |
| 70 session_manager::SessionState::ACTIVE && | |
| 71 WmShell::Get()->system_tray_delegate()->GetUserLoginStatus() != | |
| 72 LoginStatus::KIOSK_APP; | |
| 73 } | |
| 74 | |
| 75 class TitleView : public views::View, public views::ButtonListener { | |
| 76 public: | |
| 77 explicit TitleView(PaletteTray* palette_tray) : palette_tray_(palette_tray) { | |
| 78 // TODO(tdanderson|jdufault): Use TriView to handle the layout of the title. | |
| 79 // See crbug.com/614453. | |
| 80 auto* box_layout = | |
| 81 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0); | |
| 82 SetLayoutManager(box_layout); | |
| 83 | |
| 84 auto* title_label = | |
| 85 new views::Label(l10n_util::GetStringUTF16(IDS_ASH_STYLUS_TOOLS_TITLE)); | |
| 86 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
| 87 AddChildView(title_label); | |
| 88 TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::TITLE); | |
| 89 style.SetupLabel(title_label); | |
| 90 box_layout->SetFlexForView(title_label, 1); | |
| 91 if (MaterialDesignController::IsSystemTrayMenuMaterial()) { | |
| 92 help_button_ = | |
| 93 new SystemMenuButton(this, TrayPopupInkDropStyle::HOST_CENTERED, | |
| 94 kSystemMenuHelpIcon, IDS_ASH_STATUS_TRAY_HELP); | |
| 95 settings_button_ = new SystemMenuButton( | |
| 96 this, TrayPopupInkDropStyle::HOST_CENTERED, kSystemMenuSettingsIcon, | |
| 97 IDS_ASH_PALETTE_SETTINGS); | |
| 98 } else { | |
| 99 gfx::ImageSkia help_icon = | |
| 100 gfx::CreateVectorIcon(kSystemMenuHelpIcon, kMenuIconColor); | |
| 101 gfx::ImageSkia settings_icon = | |
| 102 gfx::CreateVectorIcon(kSystemMenuSettingsIcon, kMenuIconColor); | |
| 103 | |
| 104 auto* help_button = new ash::TrayPopupHeaderButton( | |
| 105 this, help_icon, IDS_ASH_STATUS_TRAY_HELP); | |
| 106 help_button->SetTooltipText( | |
| 107 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_HELP)); | |
| 108 help_button_ = help_button; | |
| 109 | |
| 110 auto* settings_button = new ash::TrayPopupHeaderButton( | |
| 111 this, settings_icon, IDS_ASH_STATUS_TRAY_SETTINGS); | |
| 112 settings_button->SetTooltipText( | |
| 113 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SETTINGS)); | |
| 114 settings_button_ = settings_button; | |
| 115 } | |
| 116 | |
| 117 AddChildView(help_button_); | |
| 118 AddChildView(settings_button_); | |
| 119 } | |
| 120 | |
| 121 ~TitleView() override {} | |
| 122 | |
| 123 private: | |
| 124 // views::ButtonListener: | |
| 125 void ButtonPressed(views::Button* sender, const ui::Event& event) override { | |
| 126 if (sender == settings_button_) { | |
| 127 palette_tray_->RecordPaletteOptionsUsage( | |
| 128 PaletteTrayOptions::PALETTE_SETTINGS_BUTTON); | |
| 129 WmShell::Get()->system_tray_controller()->ShowPaletteSettings(); | |
| 130 palette_tray_->HidePalette(); | |
| 131 } else if (sender == help_button_) { | |
| 132 palette_tray_->RecordPaletteOptionsUsage( | |
| 133 PaletteTrayOptions::PALETTE_HELP_BUTTON); | |
| 134 WmShell::Get()->system_tray_controller()->ShowPaletteHelp(); | |
| 135 palette_tray_->HidePalette(); | |
| 136 } else { | |
| 137 NOTREACHED(); | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 // Unowned pointers to button views so we can determine which button was | |
| 142 // clicked. | |
| 143 views::View* settings_button_; | |
| 144 views::View* help_button_; | |
| 145 PaletteTray* palette_tray_; | |
| 146 | |
| 147 DISALLOW_COPY_AND_ASSIGN(TitleView); | |
| 148 }; | |
| 149 | |
| 150 } // namespace | |
| 151 | |
| 152 PaletteTray::PaletteTray(WmShelf* wm_shelf) | |
| 153 : TrayBackgroundView(wm_shelf), | |
| 154 palette_tool_manager_(new PaletteToolManager(this)), | |
| 155 weak_factory_(this) { | |
| 156 PaletteTool::RegisterToolInstances(palette_tool_manager_.get()); | |
| 157 | |
| 158 if (MaterialDesignController::IsShelfMaterial()) { | |
| 159 SetInkDropMode(InkDropMode::ON); | |
| 160 SetContentsBackground(false); | |
| 161 } else { | |
| 162 SetContentsBackground(true); | |
| 163 } | |
| 164 | |
| 165 SetLayoutManager(new views::FillLayout()); | |
| 166 icon_ = new views::ImageView(); | |
| 167 UpdateTrayIcon(); | |
| 168 | |
| 169 tray_container()->SetMargin(kTrayIconMainAxisInset, kTrayIconCrossAxisInset); | |
| 170 tray_container()->AddChildView(icon_); | |
| 171 | |
| 172 WmShell::Get()->AddShellObserver(this); | |
| 173 WmShell::Get()->GetSessionStateDelegate()->AddSessionStateObserver(this); | |
| 174 ui::InputDeviceManager::GetInstance()->AddObserver(this); | |
| 175 } | |
| 176 | |
| 177 PaletteTray::~PaletteTray() { | |
| 178 if (bubble_) | |
| 179 bubble_->bubble_view()->reset_delegate(); | |
| 180 | |
| 181 ui::InputDeviceManager::GetInstance()->RemoveObserver(this); | |
| 182 WmShell::Get()->RemoveShellObserver(this); | |
| 183 WmShell::Get()->GetSessionStateDelegate()->RemoveSessionStateObserver(this); | |
| 184 } | |
| 185 | |
| 186 bool PaletteTray::PerformAction(const ui::Event& event) { | |
| 187 if (bubble_) { | |
| 188 if (num_actions_in_bubble_ == 0) | |
| 189 RecordPaletteOptionsUsage(PaletteTrayOptions::PALETTE_CLOSED_NO_ACTION); | |
| 190 HidePalette(); | |
| 191 return true; | |
| 192 } | |
| 193 | |
| 194 return ShowPalette(); | |
| 195 } | |
| 196 | |
| 197 bool PaletteTray::ShowPalette() { | |
| 198 if (bubble_) | |
| 199 return false; | |
| 200 | |
| 201 DCHECK(tray_container()); | |
| 202 | |
| 203 views::TrayBubbleView::InitParams init_params(GetAnchorAlignment(), | |
| 204 kPaletteWidth, kPaletteWidth); | |
| 205 init_params.can_activate = true; | |
| 206 init_params.close_on_deactivate = true; | |
| 207 | |
| 208 DCHECK(tray_container()); | |
| 209 | |
| 210 // The views::TrayBubbleView ctor will cause a shelf auto hide update check. | |
| 211 // Make sure to block auto hiding before that check happens. | |
| 212 should_block_shelf_auto_hide_ = true; | |
| 213 | |
| 214 // TODO(tdanderson): Refactor into common row layout code. | |
| 215 // TODO(tdanderson|jdufault): Add material design ripple effects to the menu | |
| 216 // rows. | |
| 217 | |
| 218 // Create and customize bubble view. | |
| 219 views::TrayBubbleView* bubble_view = | |
| 220 views::TrayBubbleView::Create(GetBubbleAnchor(), this, &init_params); | |
| 221 bubble_view->set_anchor_view_insets(GetBubbleAnchorInsets()); | |
| 222 bubble_view->set_margins( | |
| 223 gfx::Insets(kPalettePaddingOnTop, 0, kPalettePaddingOnBottom, 0)); | |
| 224 | |
| 225 // Add title. | |
| 226 auto* title_view = new TitleView(this); | |
| 227 title_view->SetBorder(views::CreateEmptyBorder( | |
| 228 gfx::Insets(0, kPaddingBetweenTitleAndLeftEdge, 0, 0))); | |
| 229 bubble_view->AddChildView(title_view); | |
| 230 | |
| 231 // Add horizontal separator. | |
| 232 views::Separator* separator = new views::Separator(); | |
| 233 separator->SetColor(kPaletteSeparatorColor); | |
| 234 separator->SetBorder(views::CreateEmptyBorder(gfx::Insets( | |
| 235 kPaddingBetweenTitleAndSeparator, 0, kMenuSeparatorVerticalPadding, 0))); | |
| 236 bubble_view->AddChildView(separator); | |
| 237 | |
| 238 // Add palette tools. | |
| 239 // TODO(tdanderson|jdufault): Use SystemMenuButton to get the material design | |
| 240 // ripples. | |
| 241 std::vector<PaletteToolView> views = palette_tool_manager_->CreateViews(); | |
| 242 for (const PaletteToolView& view : views) | |
| 243 bubble_view->AddChildView(view.view); | |
| 244 | |
| 245 // Show the bubble. | |
| 246 bubble_.reset(new ash::TrayBubbleWrapper(this, bubble_view)); | |
| 247 SetIsActive(true); | |
| 248 return true; | |
| 249 } | |
| 250 | |
| 251 bool PaletteTray::ContainsPointInScreen(const gfx::Point& point) { | |
| 252 if (icon_ && icon_->GetBoundsInScreen().Contains(point)) | |
| 253 return true; | |
| 254 | |
| 255 return bubble_ && bubble_->bubble_view()->GetBoundsInScreen().Contains(point); | |
| 256 } | |
| 257 | |
| 258 void PaletteTray::SessionStateChanged(session_manager::SessionState state) { | |
| 259 UpdateIconVisibility(); | |
| 260 } | |
| 261 | |
| 262 void PaletteTray::OnLockStateChanged(bool locked) { | |
| 263 UpdateIconVisibility(); | |
| 264 | |
| 265 // The user can eject the stylus during the lock screen transition, which will | |
| 266 // open the palette. Make sure to close it if that happens. | |
| 267 if (locked) | |
| 268 HidePalette(); | |
| 269 } | |
| 270 | |
| 271 void PaletteTray::ClickedOutsideBubble() { | |
| 272 if (num_actions_in_bubble_ == 0) | |
| 273 RecordPaletteOptionsUsage(PaletteTrayOptions::PALETTE_CLOSED_NO_ACTION); | |
| 274 HidePalette(); | |
| 275 } | |
| 276 | |
| 277 base::string16 PaletteTray::GetAccessibleNameForTray() { | |
| 278 return l10n_util::GetStringUTF16(IDS_ASH_STYLUS_TOOLS_TITLE); | |
| 279 } | |
| 280 | |
| 281 void PaletteTray::HideBubbleWithView(const views::TrayBubbleView* bubble_view) { | |
| 282 if (bubble_->bubble_view() == bubble_view) | |
| 283 HidePalette(); | |
| 284 } | |
| 285 | |
| 286 void PaletteTray::OnTouchscreenDeviceConfigurationChanged() { | |
| 287 UpdateIconVisibility(); | |
| 288 } | |
| 289 | |
| 290 void PaletteTray::OnStylusStateChanged(ui::StylusState stylus_state) { | |
| 291 PaletteDelegate* palette_delegate = WmShell::Get()->palette_delegate(); | |
| 292 | |
| 293 // Don't do anything if the palette should not be shown or if the user has | |
| 294 // disabled it all-together. | |
| 295 if (!IsInUserSession() || !palette_delegate->ShouldShowPalette()) | |
| 296 return; | |
| 297 | |
| 298 // Auto show/hide the palette if allowed by the user. | |
| 299 if (palette_delegate->ShouldAutoOpenPalette()) { | |
| 300 if (stylus_state == ui::StylusState::REMOVED && !bubble_) { | |
| 301 is_bubble_auto_opened_ = true; | |
| 302 ShowPalette(); | |
| 303 } else if (stylus_state == ui::StylusState::INSERTED && bubble_) { | |
| 304 HidePalette(); | |
| 305 } | |
| 306 } | |
| 307 | |
| 308 // Disable any active modes if the stylus has been inserted. | |
| 309 if (stylus_state == ui::StylusState::INSERTED) | |
| 310 palette_tool_manager_->DisableActiveTool(PaletteGroup::MODE); | |
| 311 } | |
| 312 | |
| 313 void PaletteTray::BubbleViewDestroyed() { | |
| 314 palette_tool_manager_->NotifyViewsDestroyed(); | |
| 315 SetIsActive(false); | |
| 316 } | |
| 317 | |
| 318 void PaletteTray::OnMouseEnteredView() {} | |
| 319 | |
| 320 void PaletteTray::OnMouseExitedView() {} | |
| 321 | |
| 322 base::string16 PaletteTray::GetAccessibleNameForBubble() { | |
| 323 return GetAccessibleNameForTray(); | |
| 324 } | |
| 325 | |
| 326 void PaletteTray::OnBeforeBubbleWidgetInit( | |
| 327 views::Widget* anchor_widget, | |
| 328 views::Widget* bubble_widget, | |
| 329 views::Widget::InitParams* params) const { | |
| 330 // Place the bubble in the same root window as |anchor_widget|. | |
| 331 WmWindow::Get(anchor_widget->GetNativeWindow()) | |
| 332 ->GetRootWindowController() | |
| 333 ->ConfigureWidgetInitParamsForContainer( | |
| 334 bubble_widget, kShellWindowId_SettingBubbleContainer, params); | |
| 335 } | |
| 336 | |
| 337 void PaletteTray::HideBubble(const views::TrayBubbleView* bubble_view) { | |
| 338 HideBubbleWithView(bubble_view); | |
| 339 } | |
| 340 | |
| 341 void PaletteTray::HidePalette() { | |
| 342 should_block_shelf_auto_hide_ = false; | |
| 343 is_bubble_auto_opened_ = false; | |
| 344 num_actions_in_bubble_ = 0; | |
| 345 bubble_.reset(); | |
| 346 | |
| 347 shelf()->UpdateAutoHideState(); | |
| 348 } | |
| 349 | |
| 350 void PaletteTray::HidePaletteImmediately() { | |
| 351 if (bubble_) | |
| 352 bubble_->bubble_widget()->SetVisibilityChangedAnimationsEnabled(false); | |
| 353 HidePalette(); | |
| 354 } | |
| 355 | |
| 356 void PaletteTray::RecordPaletteOptionsUsage(PaletteTrayOptions option) { | |
| 357 DCHECK_NE(option, PaletteTrayOptions::PALETTE_OPTIONS_COUNT); | |
| 358 | |
| 359 if (is_bubble_auto_opened_) { | |
| 360 UMA_HISTOGRAM_ENUMERATION("Ash.Shelf.Palette.Usage.AutoOpened", option, | |
| 361 PaletteTrayOptions::PALETTE_OPTIONS_COUNT); | |
| 362 } else { | |
| 363 UMA_HISTOGRAM_ENUMERATION("Ash.Shelf.Palette.Usage", option, | |
| 364 PaletteTrayOptions::PALETTE_OPTIONS_COUNT); | |
| 365 } | |
| 366 } | |
| 367 | |
| 368 void PaletteTray::RecordPaletteModeCancellation(PaletteModeCancelType type) { | |
| 369 if (type == PaletteModeCancelType::PALETTE_MODE_CANCEL_TYPE_COUNT) | |
| 370 return; | |
| 371 | |
| 372 UMA_HISTOGRAM_ENUMERATION( | |
| 373 "Ash.Shelf.Palette.ModeCancellation", type, | |
| 374 PaletteModeCancelType::PALETTE_MODE_CANCEL_TYPE_COUNT); | |
| 375 } | |
| 376 | |
| 377 bool PaletteTray::ShouldBlockShelfAutoHide() const { | |
| 378 return should_block_shelf_auto_hide_; | |
| 379 } | |
| 380 | |
| 381 void PaletteTray::OnActiveToolChanged() { | |
| 382 ++num_actions_in_bubble_; | |
| 383 UpdateTrayIcon(); | |
| 384 } | |
| 385 | |
| 386 WmWindow* PaletteTray::GetWindow() { | |
| 387 return shelf()->GetWindow(); | |
| 388 } | |
| 389 | |
| 390 void PaletteTray::SetShelfAlignment(ShelfAlignment alignment) { | |
| 391 if (alignment == shelf_alignment()) | |
| 392 return; | |
| 393 | |
| 394 TrayBackgroundView::SetShelfAlignment(alignment); | |
| 395 } | |
| 396 | |
| 397 void PaletteTray::AnchorUpdated() { | |
| 398 if (bubble_) | |
| 399 bubble_->bubble_view()->UpdateBubble(); | |
| 400 } | |
| 401 | |
| 402 void PaletteTray::Initialize() { | |
| 403 PaletteDelegate* delegate = WmShell::Get()->palette_delegate(); | |
| 404 // |delegate| can be null in tests. | |
| 405 if (!delegate) | |
| 406 return; | |
| 407 | |
| 408 // OnPaletteEnabledPrefChanged will get called with the initial pref value, | |
| 409 // which will take care of showing the palette. | |
| 410 palette_enabled_subscription_ = delegate->AddPaletteEnableListener(base::Bind( | |
| 411 &PaletteTray::OnPaletteEnabledPrefChanged, weak_factory_.GetWeakPtr())); | |
| 412 } | |
| 413 | |
| 414 void PaletteTray::UpdateTrayIcon() { | |
| 415 icon_->SetImage(CreateVectorIcon( | |
| 416 palette_tool_manager_->GetActiveTrayIcon( | |
| 417 palette_tool_manager_->GetActiveTool(ash::PaletteGroup::MODE)), | |
| 418 kTrayIconSize, kShelfIconColor)); | |
| 419 } | |
| 420 | |
| 421 void PaletteTray::OnPaletteEnabledPrefChanged(bool enabled) { | |
| 422 is_palette_enabled_ = enabled; | |
| 423 | |
| 424 if (!enabled) { | |
| 425 SetVisible(false); | |
| 426 palette_tool_manager_->DisableActiveTool(PaletteGroup::MODE); | |
| 427 } else { | |
| 428 UpdateIconVisibility(); | |
| 429 } | |
| 430 } | |
| 431 | |
| 432 void PaletteTray::UpdateIconVisibility() { | |
| 433 SetVisible(is_palette_enabled_ && palette_utils::HasStylusInput() && | |
| 434 IsInUserSession()); | |
| 435 } | |
| 436 | |
| 437 } // namespace ash | |
| OLD | NEW |