blob: a4ace548a7c50838272b1e439334f97af477e3ba [file] [log] [blame]
// Copyright 2021 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.
#ifndef SRC_UI_SCENIC_LIB_FOCUS_FOCUS_MANAGER_H_
#define SRC_UI_SCENIC_LIB_FOCUS_FOCUS_MANAGER_H_
#include <fuchsia/ui/focus/cpp/fidl.h>
#include <fuchsia/ui/views/cpp/fidl.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/sys/cpp/component_context.h>
#include <unordered_map>
#include "lib/inspect/cpp/inspect.h"
#include "src/ui/scenic/lib/focus/view_focuser_registry.h"
#include "src/ui/scenic/lib/focus/view_ref_focused_registry.h"
#include "src/ui/scenic/lib/view_tree/snapshot_types.h"
namespace focus {
// Provide detail on if/why focus change request was denied.
// Specific error-handling policy is responsibility of caller.
enum class FocusChangeStatus {
kAccept = 0,
kErrorRequestorInvalid,
kErrorRequestInvalid,
kErrorRequestorNotAuthorized,
kErrorRequestorNotRequestAncestor,
kErrorRequestCannotReceiveFocus,
kErrorUnhandledCase, // last
};
// Callback that should receive either the focused koid or ZX_KOID_INVALID every time the focus
// chain updates. Used by GFX to send focus events over the SessionListener.
// TODO(fxbug.dev/64376): Remove when we remove GFX input.
using LegacyFocusListener = fit::function<void(zx_koid_t, zx_koid_t)>;
// Class for tracking focus state.
class FocusManager final : public fuchsia::ui::focus::FocusChainListenerRegistry {
public:
explicit FocusManager(
inspect::Node inspect_node = inspect::Node(),
LegacyFocusListener legacy_focus_listener = [](auto, auto) {});
FocusManager(FocusManager&& other) = delete; // Disallow moving.
void Publish(sys::ComponentContext& component_context);
// Request focus transfer to the proposed ViewRef's KOID |request|, on the behalf of |requestor|.
// Return kAccept if successful.
// - If |requestor| is not authorized to focus |request|, return error.
// - If the |request| is not in |snapshot_.view_tree|, return error.
// - If the |request| is otherwise valid, but violates the focus transfer policy, return error.
FocusChangeStatus RequestFocus(zx_koid_t requestor, zx_koid_t request);
// Saves the new snapshot and updates the focus chain accordingly.
void OnNewViewTreeSnapshot(std::shared_ptr<const view_tree::Snapshot> snapshot);
// |fuchsia.ui.focus.FocusChainListenerRegistry|
void Register(
fidl::InterfaceHandle<fuchsia::ui::focus::FocusChainListener> focus_chain_listener) override;
const std::vector<zx_koid_t>& focus_chain() { return focus_chain_; }
void RegisterViewRefFocused(zx_koid_t koid,
fidl::InterfaceRequest<fuchsia::ui::views::ViewRefFocused> vrf);
void RegisterViewFocuser(zx_koid_t koid,
fidl::InterfaceRequest<fuchsia::ui::views::Focuser> focuser);
private:
// Ensure the focus chain is valid; preserve as much of the existing focus chain as possible.
// - If the focus chain is still valid, do nothing.
// - Otherwise, truncate the focus chain so that every pairwise parent-child relationship is valid
// in the current tree.
// - If the entire focus chain is invalid, the new focus chain will contain only the new root.
// - If the view tree is empty, the new focus chain is empty.
void RepairFocus();
// Transfers focus to |koid| and generates the new focus chain.
// - |koid| must be allowed to receive focus and must exist in the current view tree snapshot.
// - If the |snapshot_| is empty, then |koid| is allowed to be ZX_KOID_INVALID and will generate
// an empty focus_chain_.
void SetFocus(zx_koid_t koid);
// Replaces the focus chain with a new one. Additionally, if the new focus chain is different from
// the old one:
// - send new focus chain to all FocusChainListeners.
// - send focus gained/lost to all ViewRefFocused-type listeners.
void SetFocusChain(std::vector<zx_koid_t> update);
// Dispatches the current focus chain to all registered listeners.
void DispatchFocusChain() const;
// Dispatches the current focus chain to |listener|.
void DispatchFocusChainTo(const fuchsia::ui::focus::FocusChainListenerPtr& listener) const;
// Dispatches focus events to view clients.
void DispatchFocusEvents(zx_koid_t old_focus, zx_koid_t new_focus);
fuchsia::ui::views::ViewRef CloneViewRefOf(zx_koid_t koid) const;
fuchsia::ui::focus::FocusChain CloneFocusChain() const;
std::vector<zx_koid_t> focus_chain_;
std::shared_ptr<const view_tree::Snapshot> snapshot_ =
std::make_shared<const view_tree::Snapshot>();
fidl::BindingSet<fuchsia::ui::focus::FocusChainListenerRegistry> focus_chain_listener_registry_;
uint64_t next_focus_chain_listener_id_ = 0;
std::unordered_map<uint64_t, fuchsia::ui::focus::FocusChainListenerPtr> focus_chain_listeners_;
// TODO(fxbug.dev/64376): Remove when we remove GFX input.
const LegacyFocusListener legacy_focus_listener_;
// Manages endpoints for fuchsia.ui.views.ViewRefFocused.
ViewRefFocusedRegistry view_ref_focused_registry_;
// Manages endpoints for fuchsia.ui.views.Focuser.
ViewFocuserRegistry view_focuser_registry_;
inspect::Node inspect_node_;
inspect::LazyNode lazy_;
};
} // namespace focus
#endif // SRC_UI_SCENIC_LIB_FOCUS_FOCUS_MANAGER_H_