accelerator_history_impl.cc 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. // Copyright 2021 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/accelerators/accelerator_history_impl.h"
  5. #include "base/logging.h"
  6. #include "base/strings/string_number_conversions.h"
  7. #include "ui/events/event.h"
  8. #include "ui/events/event_target.h"
  9. namespace ash {
  10. namespace {
  11. bool ShouldFilter(ui::KeyEvent* event) {
  12. const ui::EventType type = event->type();
  13. if (!event->target() ||
  14. (type != ui::ET_KEY_PRESSED && type != ui::ET_KEY_RELEASED) ||
  15. event->is_char() || !event->target() ||
  16. // Key events with key code of VKEY_PROCESSKEY, usually created by virtual
  17. // keyboard (like handwriting input), have no effect on accelerator and
  18. // they may disturb the accelerator history. So filter them out. (see
  19. // https://crbug.com/918317)
  20. event->key_code() == ui::VKEY_PROCESSKEY) {
  21. return true;
  22. }
  23. return false;
  24. }
  25. } // namespace
  26. AcceleratorHistoryImpl::AcceleratorHistoryImpl() = default;
  27. AcceleratorHistoryImpl::~AcceleratorHistoryImpl() = default;
  28. void AcceleratorHistoryImpl::OnKeyEvent(ui::KeyEvent* event) {
  29. DCHECK(event->target());
  30. if (!ShouldFilter(event))
  31. StoreCurrentAccelerator(ui::Accelerator(*event));
  32. }
  33. void AcceleratorHistoryImpl::OnMouseEvent(ui::MouseEvent* event) {
  34. if (event->type() == ui::ET_MOUSE_PRESSED ||
  35. event->type() == ui::ET_MOUSE_RELEASED) {
  36. InterruptCurrentAccelerator();
  37. }
  38. }
  39. void AcceleratorHistoryImpl::StoreCurrentAccelerator(
  40. const ui::Accelerator& accelerator) {
  41. // Track the currently pressed keys so that we don't mistakenly store an
  42. // already pressed key as a new keypress after another key has been released.
  43. // As an example, when the user presses and holds Alt+Search, then releases
  44. // Alt but keeps holding the Search key down, at this point no new Search
  45. // presses should be stored in the history after the Alt release, since Search
  46. // was never released in the first place. crbug.com/704280.
  47. if (accelerator.key_state() == ui::Accelerator::KeyState::PRESSED) {
  48. if (!currently_pressed_keys_.emplace(accelerator.key_code()).second)
  49. return;
  50. } else {
  51. if (!currently_pressed_keys_.erase(accelerator.key_code()) &&
  52. (!last_logged_key_code_.has_value() ||
  53. *last_logged_key_code_ != accelerator.key_code())) {
  54. // Save the key code to prevent spammy logs.
  55. last_logged_key_code_ = accelerator.key_code();
  56. // If the released accelerator doesn't have a corresponding press stored,
  57. // likely the language was changed between press and release. Clear
  58. // `currently_pressed_keys_` to prevent keys being left pressed.
  59. std::string pressed_keys;
  60. for (auto key_code : currently_pressed_keys_)
  61. pressed_keys.append(base::NumberToString(key_code).append(" "));
  62. LOG(WARNING) << "Key Release (" << accelerator.key_code()
  63. << ") delivered with no corresponding Press. "
  64. "Clearing all pressed keys: "
  65. << pressed_keys;
  66. currently_pressed_keys_.clear();
  67. }
  68. }
  69. if (accelerator != current_accelerator_) {
  70. previous_accelerator_ = current_accelerator_;
  71. current_accelerator_ = accelerator;
  72. }
  73. }
  74. void AcceleratorHistoryImpl::InterruptCurrentAccelerator() {
  75. if (current_accelerator_.key_state() == ui::Accelerator::KeyState::PRESSED) {
  76. // Only interrupts pressed keys.
  77. current_accelerator_.set_interrupted_by_mouse_event(true);
  78. }
  79. }
  80. } // namespace ash