mirror of
https://github.com/phranck/TUIkit.git
synced 2026-06-20 09:54:37 +00:00
- Landing page remains at docs/index.html - DocC API documentation generated at docs/api/ - Hybrid setup: https://docs-tuikit.layered.work/ for landing page - https://docs-tuikit.layered.work/api/ for API documentation - Updated workflow to generate DocC in docs/api/
1 line
11 KiB
JSON
1 line
11 KiB
JSON
{"schemaVersion":{"minor":3,"patch":0,"major":0},"kind":"article","hierarchy":{"paths":[["doc:\/\/com.anthropic.tuikit.documentation\/documentation\/TUIKit"]]},"sections":[],"identifier":{"url":"doc:\/\/com.anthropic.tuikit.documentation\/documentation\/TUIKit\/Focus","interfaceLanguage":"swift"},"metadata":{"title":"Focus Management","roleHeading":"Article","role":"article"},"primaryContentSections":[{"kind":"content","content":[{"level":2,"type":"heading","text":"Overview","anchor":"Overview"},{"type":"paragraph","inlineContent":[{"text":"TUIKit provides a sophisticated focus management system that enables Tab\/Shift+Tab navigation, keyboard shortcuts, and custom focus logic.","type":"text"}]},{"level":2,"type":"heading","text":"The Focus Manager","anchor":"The-Focus-Manager"},{"type":"paragraph","inlineContent":[{"code":"FocusManager","type":"codeVoice"},{"text":" manages which component currently has keyboard focus:","type":"text"}]},{"type":"codeListing","syntax":"swift","code":["@Environment(\\.focusManager) var focusManager","","Button(\"Click me\") {"," \/\/ Handle click","}",".focused(focusID: \"myButton\")"]},{"level":2,"type":"heading","text":"Tab Navigation","anchor":"Tab-Navigation"},{"type":"paragraph","inlineContent":[{"text":"By default, all focusable elements can be navigated with Tab and Shift+Tab:","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"inlineContent":[{"text":"Tab","type":"text"}],"type":"strong"},{"text":": Move focus to the next element","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"Shift+Tab","type":"text"}]},{"type":"text","text":": Move focus to the previous element"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"Enter\/Space","type":"text"}]},{"type":"text","text":": Activate focused button"}]}]}]},{"type":"codeListing","syntax":"swift","code":["VStack(spacing: 1) {"," Text(\"Navigation\")",""," Button(\"First\") { }"," Button(\"Second\") { }"," Button(\"Third\") { }","}"]},{"type":"paragraph","inlineContent":[{"type":"text","text":"Users can Tab between the buttons in order."}]},{"level":2,"type":"heading","text":"Focus Indicators","anchor":"Focus-Indicators"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Focusable elements show a focus indicator when active:"}]},{"type":"codeListing","syntax":"swift","code":["Button(\"Focusable\") {"," print(\"Activated\")","}"]},{"type":"paragraph","inlineContent":[{"type":"text","text":"The button displays:"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"A border around the element"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Visual highlight (usually arrow prefix: "},{"code":"▸","type":"codeVoice"},{"type":"text","text":")"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Theme accent color","type":"text"}]}]}]},{"level":2,"type":"heading","text":"Programmatic Focus","anchor":"Programmatic-Focus"},{"type":"paragraph","inlineContent":[{"text":"Set focus programmatically:","type":"text"}]},{"type":"codeListing","syntax":"swift","code":["struct Settings: View {"," @State private var focusedField: String?",""," var body: some View {"," VStack(spacing: 1) {"," Button(\"First\") { focusedField = \"first\" }"," .focused(focusID: \"first\", focused: focusedField == \"first\")",""," Button(\"Second\") { focusedField = \"second\" }"," .focused(focusID: \"second\", focused: focusedField == \"second\")",""," Button(\"Reset\") { focusedField = nil }"," }"," }","}"]},{"level":2,"type":"heading","text":"Keyboard Events","anchor":"Keyboard-Events"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Handle keyboard events with "},{"code":"onKeyPress","type":"codeVoice"},{"type":"text","text":":"}]},{"type":"codeListing","syntax":"swift","code":["struct App: View {"," var body: some View {"," VStack {"," ContentView()"," .onKeyPress { event in"," if event.key == .character(\"h\") && event.modifiers.contains(.ctrl) {"," print(\"Help requested\")"," return true"," }"," return false"," }"," }"," }","}"]},{"level":3,"type":"heading","text":"Key Event Structure","anchor":"Key-Event-Structure"},{"type":"paragraph","inlineContent":[{"code":"KeyEvent","type":"codeVoice"},{"text":" contains:","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"key"}]},{"type":"text","text":": The key pressed (character, arrow, enter, etc.)"}]}]},{"content":[{"inlineContent":[{"inlineContent":[{"text":"modifiers","type":"text"}],"type":"strong"},{"text":": Ctrl, Shift, Alt flags","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"inlineContent":[{"type":"text","text":"raw"}],"type":"strong"},{"text":": Raw terminal escape sequence","type":"text"}],"type":"paragraph"}]}]},{"level":3,"type":"heading","text":"Special Keys","anchor":"Special-Keys"},{"type":"paragraph","inlineContent":[{"text":"Handle special keys like arrows and function keys:","type":"text"}]},{"type":"codeListing","syntax":"swift","code":[".onKeyPress { event in"," switch event.key {"," case .up:"," print(\"Arrow up\")"," case .down:"," print(\"Arrow down\")"," case .left:"," print(\"Arrow left\")"," case .right:"," print(\"Arrow right\")"," case .enter:"," print(\"Enter pressed\")"," case .escape:"," print(\"Escape pressed\")"," case .tab:"," print(\"Tab pressed\")"," case .character(let char):"," print(\"Character: \\(char)\")"," default:"," break"," }"," return true","}"]},{"level":2,"type":"heading","text":"Focusable Protocol","anchor":"Focusable-Protocol"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Components conform to "},{"code":"Focusable","type":"codeVoice"},{"type":"text","text":" to support focus:"}]},{"type":"codeListing","syntax":"swift","code":["public protocol Focusable {"," var focusID: String? { get }"," var isFocused: Bool { get }","}"]},{"type":"paragraph","inlineContent":[{"text":"Button, Menu, and other interactive components automatically implement this.","type":"text"}]},{"level":2,"type":"heading","text":"Custom Focus Logic","anchor":"Custom-Focus-Logic"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Create custom focus behavior:"}]},{"type":"codeListing","syntax":"swift","code":["struct CustomMenu: View {"," @State private var selectedIndex: Int = 0"," let items: [String]",""," var body: some View {"," VStack(spacing: 0) {"," ForEach(Array(items.enumerated()), id: \\.offset) { index, item in"," Text(item)"," .padding(1)"," .background(selectedIndex == index ? .theme.accent : .clear)"," }"," }"," .onKeyPress { event in"," switch event.key {"," case .up:"," selectedIndex = max(0, selectedIndex - 1)"," return true"," case .down:"," selectedIndex = min(items.count - 1, selectedIndex + 1)"," return true"," default:"," return false"," }"," }"," }","}"]},{"level":2,"type":"heading","text":"Focus in Modals","anchor":"Focus-in-Modals"},{"type":"paragraph","inlineContent":[{"text":"Focus works correctly with modals and overlays:","type":"text"}]},{"type":"codeListing","syntax":"swift","code":["@State var showDialog: Bool = false","","var body: some View {"," VStack {"," if showDialog {"," Alert("," title: \"Confirm\","," message: \"Continue?\""," ) {"," VStack(spacing: 1) {"," Button(\"Yes\") { showDialog = false }"," Button(\"No\") { showDialog = false }"," }"," }"," .modal()"," }",""," Button(\"Show Dialog\") { showDialog = true }"," }","}"]},{"type":"paragraph","inlineContent":[{"type":"text","text":"When a modal appears, focus automatically moves to the first focusable element in the modal."}]},{"level":2,"type":"heading","text":"Best Practices","anchor":"Best-Practices"},{"type":"orderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"Tab order"}]},{"type":"text","text":": Arrange elements logically for Tab navigation"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"Feedback"}]},{"type":"text","text":": Always provide visual feedback for focused elements"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"Shortcuts","type":"text"}],"type":"strong"},{"type":"text","text":": Implement keyboard shortcuts for common actions"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"Testing"}],"type":"strong"},{"text":": Test focus behavior with keyboard navigation","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"Accessibility","type":"text"}]},{"type":"text","text":": Consider users with motor impairments who rely on keyboard"}]}]}]},{"level":2,"type":"heading","text":"Status Bar Integration","anchor":"Status-Bar-Integration"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Status bar items show keyboard shortcuts:"}]},{"type":"codeListing","syntax":"swift","code":[".statusBarItems {"," StatusBarItem("," label: \"Help\","," shortcut: Shortcut.letter(\"?\"),"," action: {"," print(\"Show help\")"," }"," )",""," StatusBarItem("," label: \"Quit\","," shortcut: Shortcut.letter(\"q\"),"," action: {"," print(\"Exit app\")"," }"," )","}"]},{"level":2,"type":"heading","text":"Related Topics","anchor":"Related-Topics"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"FocusManager","type":"codeVoice"}]}]},{"content":[{"inlineContent":[{"code":"KeyEvent","type":"codeVoice"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Focusable"}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"View\/onKeyPress(_:)"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"code":"StatusBar","type":"codeVoice"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"StatusBarItem"}]}]}]}]}],"abstract":[{"type":"text","text":"Understand how keyboard navigation and focus works in TUIKit."}],"references":{"doc://com.anthropic.tuikit.documentation/documentation/TUIKit":{"identifier":"doc:\/\/com.anthropic.tuikit.documentation\/documentation\/TUIKit","role":"collection","type":"topic","title":"TUIKit","kind":"article","url":"\/documentation\/tuikit","abstract":[]}}} |