mirror of
https://github.com/phranck/TUIkit.git
synced 2026-06-20 09:54:37 +00:00
Feat: Modal/Alert as focus sections — replace focusManager.clear() with section activation
ModalPresentationModifier and AlertPresentationModifier now register dedicated focus sections (__modal__/__alert__) and activate them instead of clearing the entire FocusManager. Modal buttons register in the modal section. When the modal closes, the section is not re-registered and endRenderPass() falls back to the previous active section automatically.
This commit is contained in:
@@ -57,6 +57,9 @@ public struct AlertPresentationModifier<Content: View, Actions: View, Message: V
|
||||
// MARK: - Renderable
|
||||
|
||||
extension AlertPresentationModifier: Renderable {
|
||||
/// A stable section ID for alert focus sections.
|
||||
private static var alertSectionID: String { "__alert__" }
|
||||
|
||||
func renderToBuffer(context: RenderContext) -> FrameBuffer {
|
||||
// If not presented, just return base content
|
||||
guard isPresented.wrappedValue else {
|
||||
@@ -82,6 +85,8 @@ extension AlertPresentationModifier: Renderable {
|
||||
actions: { actions }
|
||||
)
|
||||
|
||||
let focusManager = context.environment.focusManager
|
||||
|
||||
// Render dimmed base with an isolated context.
|
||||
// The base content's buttons and key handlers register into a
|
||||
// throwaway FocusManager and KeyEventDispatcher so they don't
|
||||
@@ -90,11 +95,19 @@ extension AlertPresentationModifier: Renderable {
|
||||
let isolatedContext = context.isolatedForBackground()
|
||||
let dimmedBuffer = TUIkit.renderToBuffer(dimmedBase, context: isolatedContext)
|
||||
|
||||
// Clear the real focus manager so the alert's buttons become
|
||||
// the only registered focusables (auto-focus picks the first one).
|
||||
context.environment.focusManager.clear()
|
||||
// Register an alert focus section and activate it.
|
||||
// The alert section becomes the active section, so Tab/arrows
|
||||
// only navigate within the alert's focusable elements (buttons).
|
||||
let sectionID = Self.alertSectionID
|
||||
focusManager.registerSection(id: sectionID)
|
||||
focusManager.activateSection(id: sectionID)
|
||||
|
||||
let alertBuffer = TUIkit.renderToBuffer(alert, context: context)
|
||||
// Set the alert section in the context so child focusables
|
||||
// (buttons in the alert) register in the alert section.
|
||||
var alertContext = context
|
||||
alertContext.activeFocusSectionID = sectionID
|
||||
|
||||
let alertBuffer = TUIkit.renderToBuffer(alert, context: alertContext)
|
||||
|
||||
guard !dimmedBuffer.isEmpty else {
|
||||
return alertBuffer
|
||||
|
||||
@@ -42,12 +42,17 @@ public struct ModalPresentationModifier<Content: View, Modal: View>: View {
|
||||
// MARK: - Renderable
|
||||
|
||||
extension ModalPresentationModifier: Renderable {
|
||||
/// A stable section ID for modal focus sections.
|
||||
private static var modalSectionID: String { "__modal__" }
|
||||
|
||||
func renderToBuffer(context: RenderContext) -> FrameBuffer {
|
||||
// If not presented, just return base content
|
||||
guard isPresented.wrappedValue else {
|
||||
return TUIkit.renderToBuffer(content, context: context)
|
||||
}
|
||||
|
||||
let focusManager = context.environment.focusManager
|
||||
|
||||
// Render dimmed base with an isolated context.
|
||||
// The base content's buttons and key handlers register into a
|
||||
// throwaway FocusManager and KeyEventDispatcher so they don't
|
||||
@@ -56,11 +61,20 @@ extension ModalPresentationModifier: Renderable {
|
||||
let isolatedContext = context.isolatedForBackground()
|
||||
let dimmedBuffer = TUIkit.renderToBuffer(dimmedBase, context: isolatedContext)
|
||||
|
||||
// Clear the real focus manager so the modal's buttons become
|
||||
// the only registered focusables (auto-focus picks the first one).
|
||||
context.environment.focusManager.clear()
|
||||
// Register a modal focus section and activate it.
|
||||
// This replaces the previous focusManager.clear() approach.
|
||||
// The modal section becomes the active section, so Tab/arrows
|
||||
// only navigate within the modal's focusable elements.
|
||||
let sectionID = Self.modalSectionID
|
||||
focusManager.registerSection(id: sectionID)
|
||||
focusManager.activateSection(id: sectionID)
|
||||
|
||||
let modalBuffer = TUIkit.renderToBuffer(modal, context: context)
|
||||
// Set the modal section in the context so child focusables
|
||||
// (buttons in the modal) register in the modal section.
|
||||
var modalContext = context
|
||||
modalContext.activeFocusSectionID = sectionID
|
||||
|
||||
let modalBuffer = TUIkit.renderToBuffer(modal, context: modalContext)
|
||||
|
||||
guard !dimmedBuffer.isEmpty else {
|
||||
return modalBuffer
|
||||
|
||||
Reference in New Issue
Block a user