From d700731777b74e1eaa96ddebea20eb697b5cd36d Mon Sep 17 00:00:00 2001 From: phranck Date: Tue, 10 Feb 2026 21:11:34 +0100 Subject: [PATCH] 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 --- Sources/TUIkit/Views/SecureField.swift | 9 +++++-- Sources/TUIkit/Views/TextField.swift | 24 ++++++++----------- .../TUIkitExample/Pages/TextFieldPage.swift | 7 +++--- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/Sources/TUIkit/Views/SecureField.swift b/Sources/TUIkit/Views/SecureField.swift index 16c9ecbb..76c7f422 100644 --- a/Sources/TUIkit/Views/SecureField.swift +++ b/Sources/TUIkit/Views/SecureField.swift @@ -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. diff --git a/Sources/TUIkit/Views/TextField.swift b/Sources/TUIkit/Views/TextField.swift index ee7b29a8..c59c66a3 100644 --- a/Sources/TUIkit/Views/TextField.swift +++ b/Sources/TUIkit/Views/TextField.swift @@ -240,13 +240,14 @@ private struct _TextFieldCore: 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: 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: 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. diff --git a/Sources/TUIkitExample/Pages/TextFieldPage.swift b/Sources/TUIkitExample/Pages/TextFieldPage.swift index 76063906..793b11f0 100644 --- a/Sources/TUIkitExample/Pages/TextFieldPage.swift +++ b/Sources/TUIkitExample/Pages/TextFieldPage.swift @@ -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() } } }