Files
StellarX-kaifa/include/StellarX/FocusManager.h
T

101 lines
3.7 KiB
C++

/*******************************************************************************
* FocusManager.h - per-Window logical focus manager.
******************************************************************************/
#pragma once
#include "CoreTypes.h"
#include <cstddef>
#include <cstdint>
#include <vector>
class Window;
namespace StellarX
{
class FocusManager
{
struct FocusRequest
{
FocusTargetId target{};
FocusReason reason = FocusReason::Programmatic;
};
struct ScopeEntry
{
FocusScopeId id{};
FocusTargetId root{};
FocusTargetId initialFocus{};
FocusTargetId restoreFocus{};
};
::Window* window = nullptr;
FocusTargetId activeTarget{};
// Stable identity of the target whose focus callback is currently running.
// pushScope() uses it because activeTarget is intentionally empty during FocusLost.
FocusTargetId callbackFocusContext{};
bool transitionActive = false;
bool pendingPresent = false;
FocusRequest pending{};
FocusError lastError = FocusError::None;
std::vector<ScopeEntry> scopes;
std::vector<ControlHandle> invalidatedSubtrees;
std::uint64_t nextScopeValue = 2;
static constexpr std::size_t MaxReentrantRounds = 64;
bool querySnapshot(FocusTargetId target, FocusTargetSnapshot& snapshot) const;
bool isUnderInvalidatedSubtree(FocusTargetId target) const;
bool isWithinSubtree(FocusTargetId target, FocusTargetId root) const;
bool isWithinActiveScope(FocusTargetId target) const;
FocusError validateTarget(FocusTargetId target, FocusReason reason,
FocusTargetSnapshot* snapshot = nullptr) const;
FocusError validateCurrentTarget(FocusTargetId target,
FocusTargetSnapshot* snapshot = nullptr) const;
FocusPolicy requiredPolicy(FocusReason reason) const noexcept;
FocusChangeResult performTransition(FocusTargetId target, FocusReason reason);
FocusTargetId findInvalidationFallback(FocusTargetId invalidTarget) const;
std::vector<FocusTargetSnapshot> collectOrderedTargets(bool tabStopsOnly) const;
void queueReentrantRequest(FocusTargetId target, FocusReason reason) noexcept;
void normalizeInvalidScopes();
public:
explicit FocusManager(::Window& owner);
~FocusManager() = default;
FocusManager(const FocusManager&) = delete;
FocusManager& operator=(const FocusManager&) = delete;
// Normal calls switch immediately. Reentrant calls replace the per-round pending slot.
FocusChangeResult requestFocus(FocusTargetId target,
FocusReason reason = FocusReason::Programmatic);
FocusChangeResult clearFocus(FocusReason reason = FocusReason::Programmatic)
{
return requestFocus({}, reason);
}
FocusTargetId focusedTarget() const noexcept { return activeTarget; }
bool hasFocus(FocusTargetId target) const noexcept
{
return static_cast<bool>(target) && activeTarget == target;
}
FocusError getLastError() const noexcept { return lastError; }
FocusScopeId rootScope() const noexcept;
FocusScopeId activeScope() const noexcept;
// Valid Tab stops in the active scope, ordered by tabIndex and stable tree order.
std::vector<FocusTargetSnapshot> collectTabStops() const;
// Stage 2 provides scope mechanics; real Dialog integration remains in stage 4.
FocusScopeId pushScope(FocusTargetId scopeRoot, FocusTargetId initialFocus = {});
bool popScope(FocusScopeId scope);
// Called when removal is queued, before physical destruction at mutation flush.
void notifySubtreePendingRemoval(ControlHandle rootHandle);
void notifySubtreesPendingRemoval(const std::vector<ControlHandle>& rootHandles);
// Revalidates active focus after visibility, enabled-state, or policy changes.
void notifySubtreeStateChanged(ControlHandle rootHandle);
// Clears invalidation markers after the outer mutation flush.
void onMutationCommitted();
};
}