palette_tray.cc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798
  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. #include "ash/system/palette/palette_tray.h"
  5. #include <memory>
  6. #include "ash/accessibility/accessibility_controller_impl.h"
  7. #include "ash/constants/ash_features.h"
  8. #include "ash/constants/ash_pref_names.h"
  9. #include "ash/projector/projector_controller_impl.h"
  10. #include "ash/public/cpp/shelf_config.h"
  11. #include "ash/public/cpp/stylus_utils.h"
  12. #include "ash/public/cpp/system_tray_client.h"
  13. #include "ash/resources/vector_icons/vector_icons.h"
  14. #include "ash/root_window_controller.h"
  15. #include "ash/shelf/shelf.h"
  16. #include "ash/shell.h"
  17. #include "ash/strings/grit/ash_strings.h"
  18. #include "ash/style/ash_color_provider.h"
  19. #include "ash/style/icon_button.h"
  20. #include "ash/system/model/system_tray_model.h"
  21. #include "ash/system/palette/palette_tool_manager.h"
  22. #include "ash/system/palette/palette_utils.h"
  23. #include "ash/system/palette/palette_welcome_bubble.h"
  24. #include "ash/system/palette/stylus_battery_delegate.h"
  25. #include "ash/system/tray/tray_background_view.h"
  26. #include "ash/system/tray/tray_bubble_wrapper.h"
  27. #include "ash/system/tray/tray_container.h"
  28. #include "ash/system/tray/tray_popup_utils.h"
  29. #include "ash/system/tray/tray_utils.h"
  30. #include "base/bind.h"
  31. #include "base/metrics/histogram_macros.h"
  32. #include "components/pref_registry/pref_registry_syncable.h"
  33. #include "components/prefs/pref_change_registrar.h"
  34. #include "components/prefs/pref_registry_simple.h"
  35. #include "components/prefs/pref_service.h"
  36. #include "ui/accessibility/ax_enums.mojom-shared.h"
  37. #include "ui/accessibility/ax_node_data.h"
  38. #include "ui/base/l10n/l10n_util.h"
  39. #include "ui/base/resource/resource_bundle.h"
  40. #include "ui/color/color_id.h"
  41. #include "ui/compositor/layer.h"
  42. #include "ui/display/display.h"
  43. #include "ui/display/manager/display_manager.h"
  44. #include "ui/display/screen.h"
  45. #include "ui/events/devices/device_data_manager.h"
  46. #include "ui/events/devices/stylus_state.h"
  47. #include "ui/events/event_constants.h"
  48. #include "ui/gfx/paint_vector_icon.h"
  49. #include "ui/views/border.h"
  50. #include "ui/views/controls/image_view.h"
  51. #include "ui/views/controls/label.h"
  52. #include "ui/views/controls/separator.h"
  53. #include "ui/views/layout/box_layout.h"
  54. #include "ui/views/layout/fill_layout.h"
  55. namespace ash {
  56. namespace {
  57. // Padding for tray icon (dp; the button that shows the palette menu).
  58. constexpr int kTrayIconMainAxisInset = 8;
  59. constexpr int kTrayIconCrossAxisInset = 0;
  60. // Width of the palette itself (dp).
  61. constexpr int kPaletteWidth = 332;
  62. // Margins between the title view and the edges around it (dp).
  63. constexpr int kPaddingBetweenTitleAndSeparator = 3;
  64. constexpr int kPaddingBetweenBottomAndLastTrayItem = 8;
  65. // Insets for the title view (dp).
  66. constexpr auto kTitleViewPadding = gfx::Insets::TLBR(8, 16, 8, 16);
  67. // Spacing between buttons in the title view (dp).
  68. constexpr int kTitleViewChildSpacing = 16;
  69. bool HasSomeStylusDisplay() {
  70. for (const ui::TouchscreenDevice& device :
  71. ui::DeviceDataManager::GetInstance()->GetTouchscreenDevices()) {
  72. if (device.has_stylus) {
  73. return true;
  74. }
  75. }
  76. return false;
  77. }
  78. class BatteryView : public views::View {
  79. public:
  80. BatteryView() {
  81. SetLayoutManager(std::make_unique<views::BoxLayout>(
  82. views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), 4));
  83. SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
  84. icon_ = AddChildView(std::make_unique<views::ImageView>());
  85. if (stylus_battery_delegate_.IsBatteryStatusStale()) {
  86. icon_->SetImage(stylus_battery_delegate_.GetBatteryStatusUnknownImage());
  87. icon_->SetTooltipText(l10n_util::GetStringUTF16(
  88. IDS_ASH_STYLUS_BATTERY_STATUS_STALE_TOOLTIP));
  89. }
  90. label_ = AddChildView(std::make_unique<views::Label>(
  91. l10n_util::GetStringUTF16(IDS_ASH_STYLUS_BATTERY_LOW_LABEL)));
  92. label_->SetEnabledColor(stylus_battery_delegate_.GetColorForBatteryLevel());
  93. TrayPopupUtils::SetLabelFontList(label_,
  94. TrayPopupUtils::FontStyle::kSmallTitle);
  95. }
  96. // views::View:
  97. void OnThemeChanged() override {
  98. views::View::OnThemeChanged();
  99. stylus_battery_delegate_.SetBatteryUpdateCallback(base::BindRepeating(
  100. &BatteryView::OnBatteryLevelUpdated, base::Unretained(this)));
  101. OnBatteryLevelUpdated();
  102. }
  103. void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
  104. node_data->role = ax::mojom::Role::kLabelText;
  105. node_data->SetName(l10n_util::GetStringFUTF16(
  106. IDS_ASH_STYLUS_BATTERY_PERCENT_ACCESSIBLE,
  107. base::NumberToString16(
  108. stylus_battery_delegate_.battery_level().value_or(0))));
  109. }
  110. void OnBatteryLevelUpdated() {
  111. if (stylus_battery_delegate_.ShouldShowBatteryStatus() != GetVisible())
  112. SetVisible(stylus_battery_delegate_.ShouldShowBatteryStatus());
  113. icon_->SetImage(
  114. stylus_battery_delegate_.GetBatteryImage(GetColorProvider()));
  115. label_->SetVisible(stylus_battery_delegate_.IsBatteryLevelLow() &&
  116. stylus_battery_delegate_.IsBatteryStatusEligible() &&
  117. !stylus_battery_delegate_.IsBatteryStatusStale() &&
  118. !stylus_battery_delegate_.IsBatteryCharging());
  119. }
  120. private:
  121. StylusBatteryDelegate stylus_battery_delegate_;
  122. views::ImageView* icon_ = nullptr;
  123. views::Label* label_ = nullptr;
  124. };
  125. class TitleView : public views::View {
  126. public:
  127. explicit TitleView(PaletteTray* palette_tray) : palette_tray_(palette_tray) {
  128. // TODO(tdanderson|jdufault): Use TriView to handle the layout of the title.
  129. // See crbug.com/614453.
  130. auto box_layout = std::make_unique<views::BoxLayout>(
  131. views::BoxLayout::Orientation::kHorizontal, kTitleViewPadding,
  132. kTitleViewChildSpacing);
  133. box_layout->set_cross_axis_alignment(
  134. views::BoxLayout::CrossAxisAlignment::kCenter);
  135. views::BoxLayout* layout_ptr = SetLayoutManager(std::move(box_layout));
  136. auto* title_label = AddChildView(std::make_unique<views::Label>(
  137. l10n_util::GetStringUTF16(IDS_ASH_STYLUS_TOOLS_TITLE)));
  138. title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  139. title_label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
  140. AshColorProvider::ContentLayerType::kTextColorPrimary));
  141. TrayPopupUtils::SetLabelFontList(title_label,
  142. TrayPopupUtils::FontStyle::kPodMenuHeader);
  143. layout_ptr->SetFlexForView(title_label, 1);
  144. if (ash::features::IsStylusBatteryStatusEnabled()) {
  145. AddChildView(std::make_unique<BatteryView>());
  146. auto* separator = AddChildView(std::make_unique<views::Separator>());
  147. separator->SetPreferredLength(GetPreferredSize().height());
  148. separator->SetColorId(ui::kColorAshSystemUIMenuSeparator);
  149. }
  150. help_button_ = AddChildView(std::make_unique<IconButton>(
  151. base::BindRepeating(
  152. &TitleView::ButtonPressed, base::Unretained(this),
  153. PaletteTrayOptions::PALETTE_HELP_BUTTON,
  154. base::BindRepeating(
  155. &SystemTrayClient::ShowPaletteHelp,
  156. base::Unretained(Shell::Get()->system_tray_model()->client()))),
  157. IconButton::Type::kSmall, &kSystemMenuHelpIcon,
  158. IDS_ASH_STATUS_TRAY_HELP));
  159. settings_button_ = AddChildView(std::make_unique<IconButton>(
  160. base::BindRepeating(
  161. &TitleView::ButtonPressed, base::Unretained(this),
  162. PaletteTrayOptions::PALETTE_SETTINGS_BUTTON,
  163. base::BindRepeating(
  164. &SystemTrayClient::ShowPaletteSettings,
  165. base::Unretained(Shell::Get()->system_tray_model()->client()))),
  166. IconButton::Type::kSmall, &kSystemMenuSettingsIcon,
  167. IDS_ASH_PALETTE_SETTINGS));
  168. }
  169. TitleView(const TitleView&) = delete;
  170. TitleView& operator=(const TitleView&) = delete;
  171. ~TitleView() override = default;
  172. // views::View:
  173. const char* GetClassName() const override { return "TitleView"; }
  174. private:
  175. void ButtonPressed(PaletteTrayOptions option,
  176. base::RepeatingClosure callback) {
  177. palette_tray_->RecordPaletteOptionsUsage(option,
  178. PaletteInvocationMethod::MENU);
  179. std::move(callback).Run();
  180. palette_tray_->HidePalette();
  181. }
  182. // Unowned pointers to button views so we can determine which button was
  183. // clicked.
  184. views::View* settings_button_;
  185. views::View* help_button_;
  186. PaletteTray* palette_tray_;
  187. };
  188. // Used as a Shell pre-target handler to notify PaletteTray of stylus events.
  189. class StylusEventHandler : public ui::EventHandler {
  190. public:
  191. explicit StylusEventHandler(PaletteTray* tray) : palette_tray_(tray) {
  192. Shell::Get()->AddPreTargetHandler(this);
  193. }
  194. StylusEventHandler(const StylusEventHandler&) = delete;
  195. StylusEventHandler& operator=(const StylusEventHandler&) = delete;
  196. ~StylusEventHandler() override { Shell::Get()->RemovePreTargetHandler(this); }
  197. // ui::EventHandler:
  198. void OnTouchEvent(ui::TouchEvent* event) override {
  199. if (event->pointer_details().pointer_type == ui::EventPointerType::kPen) {
  200. palette_tray_->OnStylusEvent(*event);
  201. }
  202. }
  203. private:
  204. PaletteTray* palette_tray_;
  205. };
  206. } // namespace
  207. PaletteTray::PaletteTray(Shelf* shelf)
  208. : TrayBackgroundView(shelf),
  209. palette_tool_manager_(std::make_unique<PaletteToolManager>(this)),
  210. welcome_bubble_(std::make_unique<PaletteWelcomeBubble>(this)),
  211. stylus_event_handler_(std::make_unique<StylusEventHandler>(this)),
  212. scoped_session_observer_(this) {
  213. PaletteTool::RegisterToolInstances(palette_tool_manager_.get());
  214. SetLayoutManager(std::make_unique<views::FillLayout>());
  215. icon_ = new views::ImageView();
  216. icon_->SetTooltipText(l10n_util::GetStringUTF16(IDS_ASH_STYLUS_TOOLS_TITLE));
  217. UpdateTrayIcon();
  218. tray_container()->SetMargin(kTrayIconMainAxisInset, kTrayIconCrossAxisInset);
  219. tray_container()->AddChildView(icon_);
  220. Shell::Get()->AddShellObserver(this);
  221. Shell::Get()->window_tree_host_manager()->AddObserver(this);
  222. }
  223. PaletteTray::~PaletteTray() {
  224. if (bubble_)
  225. bubble_->bubble_view()->ResetDelegate();
  226. ui::DeviceDataManager::GetInstance()->RemoveObserver(this);
  227. Shell::Get()->RemoveShellObserver(this);
  228. Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
  229. }
  230. // static
  231. void PaletteTray::RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
  232. registry->RegisterBooleanPref(prefs::kHasSeenStylus, false);
  233. }
  234. // static
  235. void PaletteTray::RegisterProfilePrefs(PrefRegistrySimple* registry) {
  236. registry->RegisterBooleanPref(
  237. prefs::kEnableStylusTools, false,
  238. user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
  239. registry->RegisterBooleanPref(
  240. prefs::kLaunchPaletteOnEjectEvent, true,
  241. user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
  242. }
  243. bool PaletteTray::ContainsPointInScreen(const gfx::Point& point) {
  244. if (GetBoundsInScreen().Contains(point))
  245. return true;
  246. return bubble_ && bubble_->bubble_view()->GetBoundsInScreen().Contains(point);
  247. }
  248. bool PaletteTray::ShouldShowPalette() const {
  249. return is_palette_enabled_ && stylus_utils::HasStylusInput() &&
  250. (HasSomeStylusDisplay() ||
  251. stylus_utils::IsPaletteEnabledOnEveryDisplay());
  252. }
  253. bool PaletteTray::ShouldShowOnDisplay() {
  254. if (stylus_utils::IsPaletteEnabledOnEveryDisplay() ||
  255. display_has_stylus_for_testing_) {
  256. return true;
  257. }
  258. // |widget| is null when this function is called from PaletteTray constructor
  259. // before it is added to a widget.
  260. views::Widget* const widget = GetWidget();
  261. if (!widget)
  262. return false;
  263. const display::Display& display =
  264. display::Screen::GetScreen()->GetDisplayNearestWindow(
  265. widget->GetNativeWindow());
  266. // Is there a TouchscreenDevice which targets this display or one of
  267. // the active mirrors?
  268. display::DisplayManager* display_manager = Shell::Get()->display_manager();
  269. display::DisplayIdList ids;
  270. ids.push_back(display.id());
  271. if (display_manager->IsInMirrorMode()) {
  272. display::DisplayIdList mirrors =
  273. display_manager->GetMirroringDestinationDisplayIdList();
  274. ids.insert(ids.end(), mirrors.begin(), mirrors.end());
  275. ids.push_back(display_manager->mirroring_source_id());
  276. }
  277. for (const ui::TouchscreenDevice& device :
  278. ui::DeviceDataManager::GetInstance()->GetTouchscreenDevices()) {
  279. if (device.has_stylus && std::find(ids.begin(), ids.end(),
  280. device.target_display_id) != ids.end()) {
  281. return true;
  282. }
  283. }
  284. return false;
  285. }
  286. bool PaletteTray::IsWidgetOnInternalDisplay() {
  287. // |widget| is null when this function is called from PaletteTray constructor
  288. // before it is added to a widget.
  289. views::Widget* const widget = GetWidget();
  290. if (!widget)
  291. return false;
  292. const display::Display& display =
  293. display::Screen::GetScreen()->GetDisplayNearestWindow(
  294. widget->GetNativeWindow());
  295. return display.IsInternal();
  296. }
  297. void PaletteTray::OnStylusEvent(const ui::TouchEvent& event) {
  298. if (local_state_ && !HasSeenStylus())
  299. local_state_->SetBoolean(prefs::kHasSeenStylus, true);
  300. // Flip the enable stylus tools setting if the user has never interacted
  301. // with it. crbug/1122609
  302. if (pref_change_registrar_user_ &&
  303. !pref_change_registrar_user_->prefs()->HasPrefPath(
  304. prefs::kEnableStylusTools)) {
  305. pref_change_registrar_user_->prefs()->SetBoolean(prefs::kEnableStylusTools,
  306. true);
  307. }
  308. // Attempt to show the welcome bubble.
  309. if (!welcome_bubble_->HasBeenShown() && active_user_pref_service_) {
  310. // If a stylus event is detected on the palette tray, the user already knows
  311. // about the tray and there is no need to show them the welcome bubble.
  312. if (!GetBoundsInScreen().Contains(event.target()->GetScreenLocation(event)))
  313. welcome_bubble_->ShowIfNeeded();
  314. else
  315. welcome_bubble_->MarkAsShown();
  316. }
  317. if (HasSeenStylus() && welcome_bubble_->HasBeenShown())
  318. stylus_event_handler_.reset();
  319. }
  320. void PaletteTray::OnActiveUserPrefServiceChanged(PrefService* pref_service) {
  321. active_user_pref_service_ = pref_service;
  322. pref_change_registrar_user_ = std::make_unique<PrefChangeRegistrar>();
  323. pref_change_registrar_user_->Init(pref_service);
  324. pref_change_registrar_user_->Add(
  325. prefs::kEnableStylusTools,
  326. base::BindRepeating(&PaletteTray::OnPaletteEnabledPrefChanged,
  327. base::Unretained(this)));
  328. // Read the initial value.
  329. OnPaletteEnabledPrefChanged();
  330. // We may need to show the bubble upon switching users for devices with
  331. // external stylus, but only if the device has seen a stylus before (avoid
  332. // showing the bubble if the device has never and may never be used with
  333. // stylus).
  334. if (HasSeenStylus() && !stylus_utils::HasInternalStylus())
  335. welcome_bubble_->ShowIfNeeded();
  336. }
  337. void PaletteTray::OnSessionStateChanged(session_manager::SessionState state) {
  338. UpdateIconVisibility();
  339. if (HasSeenStylus() && !stylus_utils::HasInternalStylus())
  340. welcome_bubble_->ShowIfNeeded();
  341. }
  342. void PaletteTray::OnLockStateChanged(bool locked) {
  343. UpdateIconVisibility();
  344. if (locked) {
  345. palette_tool_manager_->DisableActiveTool(PaletteGroup::MODE);
  346. // The user can eject the stylus during the lock screen transition, which
  347. // will open the palette. Make sure to close it if that happens.
  348. HidePalette();
  349. }
  350. }
  351. void PaletteTray::OnShellInitialized() {
  352. if (features::IsProjectorEnabled()) {
  353. ProjectorControllerImpl* projector_controller =
  354. Shell::Get()->projector_controller();
  355. projector_session_observation_.Observe(
  356. projector_controller->projector_session());
  357. }
  358. }
  359. void PaletteTray::OnDisplayConfigurationChanged() {
  360. UpdateIconVisibility();
  361. }
  362. void PaletteTray::ClickedOutsideBubble() {
  363. if (num_actions_in_bubble_ == 0) {
  364. RecordPaletteOptionsUsage(PaletteTrayOptions::PALETTE_CLOSED_NO_ACTION,
  365. PaletteInvocationMethod::MENU);
  366. }
  367. HidePalette();
  368. }
  369. void PaletteTray::OnThemeChanged() {
  370. TrayBackgroundView::OnThemeChanged();
  371. UpdateTrayIcon();
  372. }
  373. std::u16string PaletteTray::GetAccessibleNameForTray() {
  374. return l10n_util::GetStringUTF16(IDS_ASH_STYLUS_TOOLS_TITLE);
  375. }
  376. void PaletteTray::HandleLocaleChange() {
  377. icon_->SetTooltipText(l10n_util::GetStringUTF16(IDS_ASH_STYLUS_TOOLS_TITLE));
  378. }
  379. void PaletteTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {
  380. if (bubble_->bubble_view() == bubble_view)
  381. HidePalette();
  382. }
  383. void PaletteTray::OnInputDeviceConfigurationChanged(
  384. uint8_t input_device_types) {
  385. if (input_device_types & ui::InputDeviceEventObserver::kTouchscreen) {
  386. UpdateIconVisibility();
  387. }
  388. }
  389. void PaletteTray::OnTouchDeviceAssociationChanged() {
  390. UpdateIconVisibility();
  391. }
  392. void PaletteTray::OnStylusStateChanged(ui::StylusState stylus_state) {
  393. // Device may have a stylus but it has been forcibly disabled.
  394. if (!stylus_utils::HasStylusInput())
  395. return;
  396. // Don't do anything if the palette tray is not shown.
  397. if (!GetVisible())
  398. return;
  399. // Only respond on the internal display.
  400. if (!IsWidgetOnInternalDisplay())
  401. return;
  402. // Auto show/hide the palette if allowed by the user.
  403. if (pref_change_registrar_user_ &&
  404. pref_change_registrar_user_->prefs()->GetBoolean(
  405. prefs::kLaunchPaletteOnEjectEvent)) {
  406. if (stylus_state == ui::StylusState::REMOVED && !bubble_) {
  407. is_bubble_auto_opened_ = true;
  408. ShowBubble();
  409. } else if (stylus_state == ui::StylusState::INSERTED && bubble_) {
  410. HidePalette();
  411. }
  412. } else if (stylus_state == ui::StylusState::REMOVED) {
  413. // Show the palette welcome bubble if the auto open palette setting is not
  414. // turned on, if the bubble has not been shown before (|welcome_bubble_|
  415. // will be nullptr if the bubble has been shown before).
  416. welcome_bubble_->ShowIfNeeded();
  417. }
  418. // Disable any active modes if the stylus has been inserted.
  419. if (stylus_state == ui::StylusState::INSERTED)
  420. palette_tool_manager_->DisableActiveTool(PaletteGroup::MODE);
  421. }
  422. void PaletteTray::BubbleViewDestroyed() {
  423. palette_tool_manager_->NotifyViewsDestroyed();
  424. // Opening the palette via an accelerator will close any open widget and then
  425. // open a new one. This method is called when the widget is closed, but due to
  426. // async close the new bubble may have already been created. If this happens,
  427. // |bubble_| will not be null.
  428. SetIsActive(bubble_ || palette_tool_manager_->GetActiveTool(
  429. PaletteGroup::MODE) != PaletteToolId::NONE);
  430. }
  431. std::u16string PaletteTray::GetAccessibleNameForBubble() {
  432. return GetAccessibleNameForTray();
  433. }
  434. bool PaletteTray::ShouldEnableExtraKeyboardAccessibility() {
  435. return Shell::Get()->accessibility_controller()->spoken_feedback().enabled();
  436. }
  437. void PaletteTray::HideBubble(const TrayBubbleView* bubble_view) {
  438. HideBubbleWithView(bubble_view);
  439. }
  440. void PaletteTray::HidePalette() {
  441. is_bubble_auto_opened_ = false;
  442. num_actions_in_bubble_ = 0;
  443. bubble_.reset();
  444. shelf()->UpdateAutoHideState();
  445. }
  446. void PaletteTray::HidePaletteImmediately() {
  447. if (bubble_)
  448. bubble_->bubble_widget()->SetVisibilityChangedAnimationsEnabled(false);
  449. HidePalette();
  450. }
  451. void PaletteTray::RecordPaletteOptionsUsage(PaletteTrayOptions option,
  452. PaletteInvocationMethod method) {
  453. DCHECK_NE(option, PaletteTrayOptions::PALETTE_OPTIONS_COUNT);
  454. if (method == PaletteInvocationMethod::SHORTCUT) {
  455. UMA_HISTOGRAM_ENUMERATION("Ash.Shelf.Palette.Usage.Shortcut", option,
  456. PaletteTrayOptions::PALETTE_OPTIONS_COUNT);
  457. } else if (is_bubble_auto_opened_) {
  458. UMA_HISTOGRAM_ENUMERATION("Ash.Shelf.Palette.Usage.AutoOpened", option,
  459. PaletteTrayOptions::PALETTE_OPTIONS_COUNT);
  460. } else {
  461. UMA_HISTOGRAM_ENUMERATION("Ash.Shelf.Palette.Usage", option,
  462. PaletteTrayOptions::PALETTE_OPTIONS_COUNT);
  463. }
  464. }
  465. void PaletteTray::RecordPaletteModeCancellation(PaletteModeCancelType type) {
  466. if (type == PaletteModeCancelType::PALETTE_MODE_CANCEL_TYPE_COUNT)
  467. return;
  468. UMA_HISTOGRAM_ENUMERATION(
  469. "Ash.Shelf.Palette.ModeCancellation", type,
  470. PaletteModeCancelType::PALETTE_MODE_CANCEL_TYPE_COUNT);
  471. }
  472. void PaletteTray::OnProjectorSessionActiveStateChanged(bool active) {
  473. is_palette_visibility_paused_ = active;
  474. if (active) {
  475. DeactivateActiveTool();
  476. SetVisiblePreferred(false);
  477. } else {
  478. UpdateIconVisibility();
  479. }
  480. }
  481. void PaletteTray::OnActiveToolChanged() {
  482. ++num_actions_in_bubble_;
  483. // If there is no tool currently active and the palette tray button was active
  484. // (eg. a mode was deactivated without pressing the palette tray button), make
  485. // the palette tray button inactive.
  486. if (palette_tool_manager_->GetActiveTool(PaletteGroup::MODE) ==
  487. PaletteToolId::NONE &&
  488. is_active()) {
  489. SetIsActive(false);
  490. }
  491. UpdateTrayIcon();
  492. }
  493. aura::Window* PaletteTray::GetWindow() {
  494. return shelf()->GetWindow();
  495. }
  496. void PaletteTray::AnchorUpdated() {
  497. if (bubble_)
  498. bubble_->bubble_view()->UpdateBubble();
  499. }
  500. void PaletteTray::Initialize() {
  501. TrayBackgroundView::Initialize();
  502. ui::DeviceDataManager::GetInstance()->AddObserver(this);
  503. InitializeWithLocalState();
  504. }
  505. bool PaletteTray::PerformAction(const ui::Event& event) {
  506. if (bubble_) {
  507. if (num_actions_in_bubble_ == 0) {
  508. RecordPaletteOptionsUsage(PaletteTrayOptions::PALETTE_CLOSED_NO_ACTION,
  509. PaletteInvocationMethod::MENU);
  510. }
  511. HidePalette();
  512. return true;
  513. }
  514. // Do not show the bubble if there was an action on the palette tray while
  515. // there was an active tool.
  516. if (DeactivateActiveTool()) {
  517. SetIsActive(false);
  518. return true;
  519. }
  520. ShowBubble();
  521. return true;
  522. }
  523. void PaletteTray::CloseBubble() {
  524. HidePalette();
  525. }
  526. void PaletteTray::ShowBubble() {
  527. if (bubble_)
  528. return;
  529. DCHECK(tray_container());
  530. // There may still be an active tool if show bubble was called from an
  531. // accelerator.
  532. DeactivateActiveTool();
  533. TrayBubbleView::InitParams init_params;
  534. init_params.delegate = GetWeakPtr();
  535. init_params.parent_window = GetBubbleWindowContainer();
  536. init_params.anchor_view = nullptr;
  537. init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
  538. init_params.anchor_rect = GetBubbleAnchor()->GetAnchorBoundsInScreen();
  539. init_params.anchor_rect.Inset(GetBubbleAnchorInsets());
  540. init_params.shelf_alignment = shelf()->alignment();
  541. init_params.preferred_width = kPaletteWidth;
  542. init_params.close_on_deactivate = true;
  543. init_params.translucent = true;
  544. init_params.corner_radius = kTrayItemCornerRadius;
  545. init_params.reroute_event_handler = true;
  546. // TODO(tdanderson): Refactor into common row layout code.
  547. // TODO(tdanderson|jdufault): Add material design ripple effects to the menu
  548. // rows.
  549. // Create and customize bubble view.
  550. TrayBubbleView* bubble_view = new TrayBubbleView(init_params);
  551. bubble_view->set_margins(GetSecondaryBubbleInsets());
  552. bubble_view->SetBorder(views::CreateEmptyBorder(
  553. gfx::Insets::TLBR(0, 0, kPaddingBetweenBottomAndLastTrayItem, 0)));
  554. auto setup_layered_view = [](views::View* view) {
  555. // In dark light mode, we switch TrayBubbleView to use a textured layer
  556. // instead of solid color layer, so no need to create an extra layer here.
  557. if (features::IsDarkLightModeEnabled())
  558. return;
  559. view->SetPaintToLayer();
  560. view->layer()->SetFillsBoundsOpaquely(false);
  561. };
  562. // Add title.
  563. auto* title_view =
  564. bubble_view->AddChildView(std::make_unique<TitleView>(this));
  565. setup_layered_view(title_view);
  566. // Add horizontal separator between the title and tools.
  567. auto* separator =
  568. bubble_view->AddChildView(std::make_unique<views::Separator>());
  569. setup_layered_view(separator);
  570. separator->SetColorId(ui::kColorAshSystemUIMenuSeparator);
  571. separator->SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
  572. kPaddingBetweenTitleAndSeparator, 0, kMenuSeparatorVerticalPadding, 0)));
  573. // Add palette tools.
  574. // TODO(tdanderson|jdufault): Use SystemMenuButton to get the material design
  575. // ripples.
  576. std::vector<PaletteToolView> views = palette_tool_manager_->CreateViews();
  577. for (const PaletteToolView& view : views) {
  578. bubble_view->AddChildView(view.view);
  579. setup_layered_view(view.view);
  580. }
  581. // Show the bubble.
  582. bubble_ = std::make_unique<TrayBubbleWrapper>(this, bubble_view);
  583. SetIsActive(true);
  584. }
  585. TrayBubbleView* PaletteTray::GetBubbleView() {
  586. return bubble_ ? bubble_->bubble_view() : nullptr;
  587. }
  588. views::Widget* PaletteTray::GetBubbleWidget() const {
  589. return bubble_ ? bubble_->GetBubbleWidget() : nullptr;
  590. }
  591. const char* PaletteTray::GetClassName() const {
  592. return "PaletteTray";
  593. }
  594. void PaletteTray::InitializeWithLocalState() {
  595. DCHECK(!local_state_);
  596. local_state_ = Shell::Get()->local_state();
  597. // |local_state_| could be null in tests.
  598. if (!local_state_)
  599. return;
  600. // If a device has an internal stylus or the flag to force stylus is set, mark
  601. // the has seen stylus flag as true since we know the user has a stylus.
  602. if (stylus_utils::HasInternalStylus() ||
  603. stylus_utils::HasForcedStylusInput()) {
  604. local_state_->SetBoolean(prefs::kHasSeenStylus, true);
  605. }
  606. pref_change_registrar_local_ = std::make_unique<PrefChangeRegistrar>();
  607. pref_change_registrar_local_->Init(local_state_);
  608. pref_change_registrar_local_->Add(
  609. prefs::kHasSeenStylus,
  610. base::BindRepeating(&PaletteTray::OnHasSeenStylusPrefChanged,
  611. base::Unretained(this)));
  612. OnHasSeenStylusPrefChanged();
  613. }
  614. void PaletteTray::UpdateTrayIcon() {
  615. icon_->SetImage(CreateVectorIcon(
  616. palette_tool_manager_->GetActiveTrayIcon(
  617. palette_tool_manager_->GetActiveTool(PaletteGroup::MODE)),
  618. kTrayIconSize,
  619. AshColorProvider::Get()->GetContentLayerColor(
  620. AshColorProvider::ContentLayerType::kIconColorPrimary)));
  621. }
  622. void PaletteTray::OnPaletteEnabledPrefChanged() {
  623. is_palette_enabled_ = pref_change_registrar_user_->prefs()->GetBoolean(
  624. prefs::kEnableStylusTools);
  625. if (!is_palette_enabled_) {
  626. SetVisiblePreferred(false);
  627. palette_tool_manager_->DisableActiveTool(PaletteGroup::MODE);
  628. } else {
  629. UpdateIconVisibility();
  630. }
  631. }
  632. void PaletteTray::OnHasSeenStylusPrefChanged() {
  633. DCHECK(local_state_);
  634. UpdateIconVisibility();
  635. }
  636. bool PaletteTray::DeactivateActiveTool() {
  637. PaletteToolId active_tool_id =
  638. palette_tool_manager_->GetActiveTool(PaletteGroup::MODE);
  639. if (active_tool_id != PaletteToolId::NONE) {
  640. palette_tool_manager_->DeactivateTool(active_tool_id);
  641. RecordPaletteModeCancellation(PaletteToolIdToPaletteModeCancelType(
  642. active_tool_id, false /*is_switched*/));
  643. return true;
  644. }
  645. return false;
  646. }
  647. bool PaletteTray::HasSeenStylus() {
  648. return local_state_ && local_state_->GetBoolean(prefs::kHasSeenStylus);
  649. }
  650. void PaletteTray::SetDisplayHasStylusForTesting() {
  651. display_has_stylus_for_testing_ = true;
  652. UpdateIconVisibility();
  653. }
  654. void PaletteTray::UpdateIconVisibility() {
  655. bool visible_preferred =
  656. is_palette_enabled_ && !is_palette_visibility_paused_ &&
  657. stylus_utils::HasStylusInput() && ShouldShowOnDisplay() &&
  658. palette_utils::IsInUserSession();
  659. SetVisiblePreferred(visible_preferred);
  660. if (visible_preferred)
  661. UpdateLayout();
  662. }
  663. } // namespace ash