Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions Sources/SwiftTUI/Views/Controls/TextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public struct TextField: View, PrimitiveView {
public let action: (String) -> Void

@Environment(\.placeholderColor) private var placeholderColor: Color
@Environment(\.textFieldCursorStyle) private var cursorStyle: TextFieldCursorStyle

public init(placeholder: String? = nil, action: @escaping (String) -> Void) {
self.placeholder = placeholder
Expand All @@ -15,25 +16,29 @@ public struct TextField: View, PrimitiveView {

func buildNode(_ node: Node) {
setupEnvironmentProperties(node: node)
node.control = TextFieldControl(placeholder: placeholder ?? "", placeholderColor: placeholderColor, action: action)
node.control = TextFieldControl(placeholder: placeholder ?? "", placeholderColor: placeholderColor, cursorStyle: cursorStyle, action: action)
}

func updateNode(_ node: Node) {
setupEnvironmentProperties(node: node)
node.view = self
(node.control as! TextFieldControl).action = action
let control = node.control as! TextFieldControl
control.action = action
control.cursorStyle = cursorStyle
}

private class TextFieldControl: Control {
var placeholder: String
var placeholderColor: Color
var cursorStyle: TextFieldCursorStyle
var action: (String) -> Void

var text: String = ""

init(placeholder: String, placeholderColor: Color, action: @escaping (String) -> Void) {
init(placeholder: String, placeholderColor: Color, cursorStyle: TextFieldCursorStyle, action: @escaping (String) -> Void) {
self.placeholder = placeholder
self.placeholderColor = placeholderColor
self.cursorStyle = cursorStyle
self.action = action
}

Expand Down Expand Up @@ -65,17 +70,26 @@ public struct TextField: View, PrimitiveView {
guard position.line == 0 else { return nil }
if text.isEmpty {
if position.column.intValue < placeholder.count {
let showUnderline = (position.column.intValue == 0) && isFirstResponder
let showCursor = (position.column.intValue == 0) && isFirstResponder
let char = placeholder[placeholder.index(placeholder.startIndex, offsetBy: position.column.intValue)]
return Cell(
char: char,
foregroundColor: placeholderColor,
attributes: CellAttributes(underline: showUnderline)
attributes: cursorStyle == .block
? CellAttributes(inverted: showCursor)
: CellAttributes(underline: showCursor)
)
}
return .init(char: " ")
}
if position.column.intValue == text.count, isFirstResponder { return Cell(char: " ", attributes: CellAttributes(underline: true)) }
if position.column.intValue == text.count, isFirstResponder {
return Cell(
char: " ",
attributes: cursorStyle == .block
? CellAttributes(inverted: true)
: CellAttributes(underline: true)
)
}
guard position.column.intValue < text.count else { return .init(char: " ") }
return Cell(char: text[text.index(text.startIndex, offsetBy: position.column.intValue)])
}
Expand Down
33 changes: 33 additions & 0 deletions Sources/SwiftTUI/Views/Modifiers/TextFieldCursorStyle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Foundation

/// The visual style of the cursor in a TextField
public enum TextFieldCursorStyle {
/// Cursor displayed as an underline beneath the character
case underline
/// Cursor displayed as an inverted block covering the character
case block
}

// MARK: - Environment Value

public extension EnvironmentValues {
var textFieldCursorStyle: TextFieldCursorStyle {
get { self[TextFieldCursorStyleKey.self] }
set { self[TextFieldCursorStyleKey.self] = newValue }
}
}

private struct TextFieldCursorStyleKey: EnvironmentKey {
static var defaultValue: TextFieldCursorStyle { .underline }
}

// MARK: - View Modifier

public extension View {
/// Sets the cursor style for TextFields within this view
/// - Parameter style: The cursor style to use (.underline or .block)
/// - Returns: A view with the specified cursor style
func textFieldCursorStyle(_ style: TextFieldCursorStyle) -> some View {
environment(\.textFieldCursorStyle, style)
}
}