// // UIFocusSystem.cpp // SDLTest // // Created by Даниил Виноградов on 02.04.2023. // #include #include #include #include #include namespace NXKit { UIFocusSystem::UIFocusSystem() = default; void UIFocusSystem::setActive(bool active) { if (_isActive != active) { _isActive = active; // updateFocus(); UIFocusUpdateContext context; context._previouslyFocusedItem = _focusedItem; context._nextFocusedItem = _isActive ? _focusedItem : std::weak_ptr(); context._focusHeading = UIFocusHeading::none; applyFocusToItem(_focusedItem.lock(), context); } } void UIFocusSystem::sendEvent(const std::shared_ptr& event) { auto pevent = std::dynamic_pointer_cast(event); if (pevent == nullptr) return setActive(false); if (!_isActive) { setActive(true); return; } std::shared_ptr press; for (const auto& _press: pevent->allPresses()) { if (_press->type() == UIPressType::select) { press = _press; } } if (press != nullptr && !focusedItem().expired()) { auto focusedView = std::static_pointer_cast(focusedItem().lock()); if (focusedView) { if (press->phase() == UIPressPhase::began) { focusedView->pressesBegan(pevent->allPresses(), pevent); _selectedFocusedItem = focusedView; } else if (press->phase() == UIPressPhase::ended) { focusedView->pressesEnded(pevent->allPresses(), pevent); _selectedFocusedItem.reset(); } } return; } UIFocusUpdateContext context; context._previouslyFocusedItem = _focusedItem; context._focusHeading = makeFocusHeadingFromEvent(pevent); if (context._focusHeading == UIFocusHeading::none) return; std::weak_ptr nextItem; if (focusedItem().expired()) nextItem = _rootWindow.lock()->searchForFocus(); else { auto current = std::dynamic_pointer_cast(focusedItem().lock()); if (current->superview().expired()) { nextItem = _rootWindow.lock()->searchForFocus(); } else { std::shared_ptr potencianNextItem; while (true) { potencianNextItem = current->superview().lock()->getNextFocusItem(current, context._focusHeading); if (!potencianNextItem) { // if no next item to focus if (!current->superview().lock()->superview().expired()) { // but item has parent, check parent current = current->superview().lock(); continue; } else { // else stop searching break; } } context._nextFocusedItem = potencianNextItem; bool currentIsFine = context.previouslyFocusedItem().expired() || context.previouslyFocusedItem().lock()->shouldUpdateFocusIn(context); bool nextIsFine = context.nextFocusedItem().expired() || context.nextFocusedItem().lock()->shouldUpdateFocusIn(context); if (currentIsFine && nextIsFine) { break; } } nextItem = potencianNextItem; } } if (nextItem.expired()) { nextItem = _focusedItem; } context._nextFocusedItem = nextItem; applyFocusToItem(nextItem.lock(), context); } void UIFocusSystem::updateFocus() { auto item = _rootWindow.lock()->searchForFocus(); UIFocusUpdateContext context; context._previouslyFocusedItem = _focusedItem; context._nextFocusedItem = _isActive ? item : std::weak_ptr(); context._focusHeading = UIFocusHeading::none; applyFocusToItem(item, context); } std::shared_ptr UIFocusSystem::findItemToFocus() { auto vc = _rootWindow.lock()->rootViewController(); while (!vc->children().empty()) { vc = vc->children().front(); } return vc->view(); } void UIFocusSystem::applyFocusToItem(const std::shared_ptr& item, UIFocusUpdateContext context) { UIFocusAnimationCoordinator coordinator; _focusedItem = item; if (context.previouslyFocusedItem().lock() == context.nextFocusedItem().lock()) { if (!context.previouslyFocusedItem().expired()) { context.previouslyFocusedItem().lock()->didUpdateFocusIn(context, &coordinator); } } else { if (!context.previouslyFocusedItem().expired()) { context.previouslyFocusedItem().lock()->didUpdateFocusIn(context, &coordinator); } if (!context.nextFocusedItem().expired()) { context.nextFocusedItem().lock()->didUpdateFocusIn(context, &coordinator); } } UIFocusAnimationContext animationContext; UIView::animate(animationContext.duration(), 0, curveEaseOut, [context, coordinator, animationContext]() { for (const auto& animation: coordinator._coordinatedAnimations) { animation(); } for (const auto& animation: coordinator._coordinatedFocusingAnimations) { animation(animationContext); } for (const auto& animation: coordinator._coordinatedUnfocusingAnimations) { animation(animationContext); } }, [coordinator](bool res) { for (const auto& completion: coordinator._coordinatedAnimationCompletions) { completion(); } }); } UIFocusHeading UIFocusSystem::makeFocusHeadingFromEvent(const std::shared_ptr& event) { if (event == nullptr) return UIFocusHeading::none; for (const auto& press: event->allPresses()) { if (press->phase() != UIPressPhase::began) continue; auto type = press->type(); if (type == UIPressType::rightArrow) { return UIFocusHeading::right; } if (type == UIPressType::upArrow) { return UIFocusHeading::up; } if (type == UIPressType::leftArrow) { return UIFocusHeading::left; } if (type == UIPressType::downArrow) { return UIFocusHeading::down; } } return UIFocusHeading::none; } }