blob: 39c428fcfd8e84435e9614b7b304dcd29e9a8b6e [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 "src/ui/a11y/lib/focus_chain/focus_chain_manager.h"
#include <lib/async/default.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include "src/ui/a11y/lib/util/util.h"
namespace a11y {
FocusChainManager::FocusChainManager(std::shared_ptr<AccessibilityViewInterface> a11y_view)
: a11y_view_(a11y_view) {
FX_DCHECK(a11y_view_);
}
FocusChainManager::ViewRefWatcher::ViewRefWatcher(fuchsia::ui::views::ViewRef view_ref,
OnViewRefInvalidatedCallback callback)
: view_ref_(std::move(view_ref)),
callback_(std::move(callback)),
wait_(this, view_ref_.reference.get(), ZX_EVENTPAIR_PEER_CLOSED) {
FX_CHECK(wait_.Begin(async_get_default_dispatcher()) == ZX_OK);
}
FocusChainManager::ViewRefWatcher::~ViewRefWatcher() = default;
void FocusChainManager::OnFocusChange(fuchsia::ui::focus::FocusChain focus_chain,
OnFocusChangeCallback callback) {
focus_chain_.clear();
if (focus_chain.has_focus_chain()) {
auto& new_focus_chain = *(focus_chain.mutable_focus_chain());
for (auto& view_ref : new_focus_chain) {
auto view_ref_watcher = std::make_unique<ViewRefWatcher>(
std::move(view_ref), [this]() { InvalidateFocusChain(); });
focus_chain_.push_back(std::move(view_ref_watcher));
}
}
Notify();
callback();
}
void FocusChainManager::Register(fxl::WeakPtr<AccessibilityFocusChainListener> listener) {
// On registration, sends the listener the current focus.
if (listener) {
listener->OnViewFocus(GetFocusedView());
listeners_.push_back(std::move(listener));
}
}
void FocusChainManager::InvalidateFocusChain() {
focus_chain_.clear();
Notify();
}
zx_koid_t FocusChainManager::GetFocusedView() const {
if (focus_chain_.empty()) {
return ZX_KOID_INVALID;
}
const auto& focused_view = focus_chain_.back();
return focused_view->koid();
}
void FocusChainManager::Notify() {
zx_koid_t focused_view = GetFocusedView();
auto it = listeners_.begin();
while (it != listeners_.end()) {
if (*it) {
(*it)->OnViewFocus(focused_view);
++it;
} else {
// This listener is no longer listening for updates.
it = listeners_.erase(it);
}
}
}
zx_koid_t FocusChainManager::ViewRefWatcher::koid() const { return GetKoid(view_ref_); }
void FocusChainManager::ViewRefWatcher::OnViewRefSignaled(async_dispatcher_t* dispatcher,
async::WaitBase* wait, zx_status_t status,
const zx_packet_signal* signal) {
// The only signal we are waiting for is ZX_EVENTPAIR_PEER_CLOSED.
callback_();
}
void FocusChainManager::ChangeFocusToView(fuchsia::ui::views::ViewRef view_ref,
ChangeFocusToViewCallback callback) {
a11y_view_->RequestFocus(std::move(view_ref), [callback = std::move(callback)](auto result) {
if (result.is_err()) {
callback(false);
} else {
callback(true);
}
});
return;
}
} // namespace a11y