Fix: TextField expands to fill width, label is accessibility-only

- TextField now expands to fill available width (SwiftUI behavior)
- Label parameter is for accessibility only, not rendered visually
- Use prompt: parameter for placeholder text
- Update example app to use prompt: correctly
- SecureField also expands to fill available width
This commit is contained in:
phranck
2026-02-10 21:11:34 +01:00
parent f8e255da06
commit d700731777
3 changed files with 21 additions and 19 deletions
+7 -2
View File
@@ -197,8 +197,13 @@ private struct _SecureFieldCore: View, Renderable {
let palette = context.environment.palette
let cursorStyle = context.environment.textCursorStyle
// SecureField uses fixed default width. Use .frame() to change size.
let contentWidth = defaultContentWidth
// SecureField expands to fill available width (like SwiftUI).
let contentWidth: Int
if context.availableWidth > 0 {
contentWidth = context.availableWidth
} else {
contentWidth = defaultContentWidth
}
// Get or create persistent focusID from state storage.
// focusID must be stable across renders for focus state to persist.
+10 -14
View File
@@ -240,13 +240,14 @@ private struct _TextFieldCore<Label: View>: View, Renderable {
let palette = context.environment.palette
let cursorStyle = context.environment.textCursorStyle
// Render the label first to know its width
let labelBuffer = TUIkit.renderToBuffer(label, context: context)
let labelWidth = labelBuffer.width
let labelText = labelBuffer.lines.first ?? ""
// TextField uses fixed default width. Use .frame() to change size.
let contentWidth = defaultContentWidth
// TextField expands to fill available width (like SwiftUI).
// The label is for accessibility only and not rendered visually.
let contentWidth: Int
if context.availableWidth > 0 {
contentWidth = context.availableWidth
} else {
contentWidth = defaultContentWidth
}
// Get or create persistent focusID from state storage.
// focusID must be stable across renders for focus state to persist.
@@ -283,7 +284,7 @@ private struct _TextFieldCore<Label: View>: View, Renderable {
// Determine focus state
let isFocused = focusManager.isFocused(id: persistedFocusID)
// Build the text field content
// Build the text field content (label is not rendered - it's for accessibility only)
let fieldContent = buildContent(
handler: handler,
isFocused: isFocused,
@@ -293,12 +294,7 @@ private struct _TextFieldCore<Label: View>: View, Renderable {
contentWidth: contentWidth
)
// Combine label and field content
if labelWidth > 0 {
return FrameBuffer(text: labelText + " " + fieldContent)
} else {
return FrameBuffer(text: fieldContent)
}
return FrameBuffer(text: fieldContent)
}
/// Builds the rendered text field content.
@@ -74,11 +74,11 @@ struct TextFieldPage: View {
VStack(alignment: .leading, spacing: 1) {
HStack(spacing: 1) {
Text("Input:").foregroundStyle(.palette.foregroundSecondary)
TextField("Type here...", text: $demoText)
TextField("Input", text: $demoText, prompt: Text("Type here..."))
}
HStack(spacing: 1) {
Text("Search:").foregroundStyle(.palette.foregroundSecondary)
TextField("Search", text: $searchQuery)
TextField("Search", text: $searchQuery, prompt: Text("Enter search term..."))
.onSubmit { submittedValue = searchQuery }
}
if !submittedValue.isEmpty {
@@ -95,7 +95,8 @@ struct TextFieldPage: View {
VStack(alignment: .leading, spacing: 1) {
HStack(spacing: 1) {
Text("Disabled:").foregroundStyle(.palette.foregroundSecondary)
TextField("Disabled", text: $disabledText).disabled()
TextField("Disabled", text: $disabledText, prompt: Text("Cannot edit"))
.disabled()
}
}
}