| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/accelerators/key_hold_detector.h" |
| |
| #include <utility> |
| |
| #include "ash/shell.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "ui/aura/window_tracker.h" |
| #include "ui/aura/window_tree_host.h" |
| #include "ui/events/event_dispatcher.h" |
| #include "ui/events/event_sink.h" |
| |
| namespace ash { |
| namespace { |
| |
| void DispatchPressedEvent(const ui::KeyEvent& key_event, |
| std::unique_ptr<aura::WindowTracker> tracker) { |
| // The target window may be gone. |
| if (tracker->windows().empty()) |
| return; |
| ui::KeyEvent event(key_event); |
| aura::Window* target = *(tracker->windows().begin()); |
| ignore_result(target->GetHost()->event_sink()->OnEventFromSource(&event)); |
| } |
| |
| void PostPressedEvent(ui::KeyEvent* event) { |
| // Modify RELEASED event to PRESSED event. |
| const ui::KeyEvent pressed_event( |
| ui::ET_KEY_PRESSED, event->key_code(), event->code(), |
| event->flags() | ui::EF_SHIFT_DOWN | ui::EF_IS_SYNTHESIZED); |
| std::unique_ptr<aura::WindowTracker> tracker(new aura::WindowTracker); |
| tracker->Add(static_cast<aura::Window*>(event->target())); |
| |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::Bind(&DispatchPressedEvent, pressed_event, base::Passed(&tracker))); |
| } |
| |
| } // namespace |
| |
| KeyHoldDetector::KeyHoldDetector(std::unique_ptr<Delegate> delegate) |
| : state_(INITIAL), delegate_(std::move(delegate)) {} |
| |
| KeyHoldDetector::~KeyHoldDetector() {} |
| |
| void KeyHoldDetector::OnKeyEvent(ui::KeyEvent* event) { |
| if (!delegate_->ShouldProcessEvent(event)) |
| return; |
| |
| if (delegate_->IsStartEvent(event)) { |
| switch (state_) { |
| case INITIAL: |
| // Pass through posted event. |
| if (event->flags() & ui::EF_IS_SYNTHESIZED) { |
| event->set_flags(event->flags() & ~ui::EF_IS_SYNTHESIZED); |
| return; |
| } |
| state_ = PRESSED; |
| if (delegate_->ShouldStopEventPropagation()) { |
| // Don't process ET_KEY_PRESSED event yet. The ET_KEY_PRESSED |
| // event will be generated upon ET_KEY_RELEASEED event below. |
| event->StopPropagation(); |
| } |
| break; |
| case PRESSED: |
| state_ = HOLD; |
| // pass through |
| case HOLD: |
| delegate_->OnKeyHold(event); |
| if (delegate_->ShouldStopEventPropagation()) |
| event->StopPropagation(); |
| break; |
| } |
| } else if (event->type() == ui::ET_KEY_RELEASED) { |
| switch (state_) { |
| case INITIAL: |
| break; |
| case PRESSED: { |
| if (delegate_->ShouldStopEventPropagation()) { |
| PostPressedEvent(event); |
| event->StopPropagation(); |
| } |
| break; |
| } |
| case HOLD: { |
| delegate_->OnKeyUnhold(event); |
| if (delegate_->ShouldStopEventPropagation()) |
| event->StopPropagation(); |
| break; |
| } |
| } |
| state_ = INITIAL; |
| } |
| } |
| |
| } // namespace ash |