blob: 92d4294f5d6481c7d3ca344766c494783f45c2d4 [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/logger.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include "src/ui/a11y/lib/util/util.h"
namespace a11y {
FocusChainManager::FocusChainManager(fuchsia::ui::views::FocuserPtr focuser,
SemanticsSource* semantics_source)
: focuser_(std::move(focuser)), semantics_source_(semantics_source) {
FX_DCHECK(semantics_source_);
}
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();
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(zx_koid_t view_ref_koid,
ChangeFocusToViewCallback callback) {
if (!semantics_source_->ViewHasSemantics(view_ref_koid)) {
callback(false);
return;
}
auto view_ref = semantics_source_->ViewRefClone(view_ref_koid);
focuser_->RequestFocus(std::move(*view_ref), [callback = std::move(callback)](auto result) {
if (result.is_err()) {
callback(false);
} else {
callback(true);
}
});
return;
}
} // namespace a11y