Support smaller primitive views
This commit is contained in:
parent
0186b9dcf5
commit
312a8a7925
|
@ -19,8 +19,8 @@
|
|||
/// 1. `View.makeMountedView`
|
||||
/// 2. `MountedHostView.update` when reconciling
|
||||
///
|
||||
protocol EnvironmentReader {
|
||||
mutating func setContent(from values: EnvironmentValues)
|
||||
public protocol _EnvironmentReader {
|
||||
mutating func _setContent(from values: EnvironmentValues)
|
||||
}
|
||||
|
||||
@propertyWrapper
|
||||
|
@ -37,7 +37,7 @@ public struct Environment<Value>: DynamicProperty {
|
|||
self.keyPath = keyPath
|
||||
}
|
||||
|
||||
mutating func setContent(from values: EnvironmentValues) {
|
||||
public mutating func _setContent(from values: EnvironmentValues) {
|
||||
content = .value(values[keyPath: keyPath])
|
||||
}
|
||||
|
||||
|
@ -52,4 +52,4 @@ public struct Environment<Value>: DynamicProperty {
|
|||
}
|
||||
}
|
||||
|
||||
extension Environment: EnvironmentReader {}
|
||||
extension Environment: _EnvironmentReader {}
|
||||
|
|
|
@ -40,7 +40,7 @@ public struct EnvironmentObject<ObjectType>: DynamicProperty
|
|||
var _store: ObjectType?
|
||||
var _seed: Int = 0
|
||||
|
||||
mutating func setContent(from values: EnvironmentValues) {
|
||||
public mutating func _setContent(from values: EnvironmentValues) {
|
||||
_store = values[ObjectIdentifier(ObjectType.self)]
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ public struct EnvironmentObject<ObjectType>: DynamicProperty
|
|||
public init() {}
|
||||
}
|
||||
|
||||
extension EnvironmentObject: ObservedProperty, EnvironmentReader {}
|
||||
extension EnvironmentObject: ObservedProperty, _EnvironmentReader {}
|
||||
|
||||
extension ObservableObject {
|
||||
static var environmentStore: WritableKeyPath<EnvironmentValues, Self?> {
|
||||
|
|
|
@ -22,7 +22,7 @@ public protocol EnvironmentalModifier: ViewModifier {
|
|||
static var _requiresMainThread: Bool { get }
|
||||
}
|
||||
|
||||
private struct EnvironmentalModifierResolver<M>: ViewModifier, EnvironmentReader
|
||||
private struct EnvironmentalModifierResolver<M>: ViewModifier, _EnvironmentReader
|
||||
where M: EnvironmentalModifier
|
||||
{
|
||||
let modifier: M
|
||||
|
@ -32,7 +32,7 @@ private struct EnvironmentalModifierResolver<M>: ViewModifier, EnvironmentReader
|
|||
content.modifier(resolved)
|
||||
}
|
||||
|
||||
mutating func setContent(from values: EnvironmentValues) {
|
||||
mutating func _setContent(from values: EnvironmentValues) {
|
||||
resolved = modifier.resolve(in: values)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -309,8 +309,8 @@ public extension FiberReconciler {
|
|||
storage.getter = { box.value }
|
||||
value = storage
|
||||
// Read from the environment.
|
||||
} else if var environmentReader = value as? EnvironmentReader {
|
||||
environmentReader.setContent(from: environment)
|
||||
} else if var environmentReader = value as? _EnvironmentReader {
|
||||
environmentReader._setContent(from: environment)
|
||||
value = environmentReader
|
||||
}
|
||||
// Subscribe to observable properties.
|
||||
|
@ -322,8 +322,8 @@ public extension FiberReconciler {
|
|||
}
|
||||
property.set(value: value, on: &content)
|
||||
}
|
||||
if var environmentReader = content as? EnvironmentReader {
|
||||
environmentReader.setContent(from: environment)
|
||||
if var environmentReader = content as? _EnvironmentReader {
|
||||
environmentReader._setContent(from: environment)
|
||||
content = environmentReader
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,7 +127,8 @@ extension EnvironmentValues {
|
|||
}
|
||||
}
|
||||
|
||||
var measureText: (Text, ProposedViewSize, EnvironmentValues) -> CGSize {
|
||||
@_spi(TokamakCore)
|
||||
public var measureText: (Text, ProposedViewSize, EnvironmentValues) -> CGSize {
|
||||
get { self[MeasureTextKey.self] }
|
||||
set { self[MeasureTextKey.self] = newValue }
|
||||
}
|
||||
|
|
|
@ -37,9 +37,9 @@ extension ModifiedContent: ModifierContainer {
|
|||
var environmentModifier: _EnvironmentModifier? { modifier as? _EnvironmentModifier }
|
||||
}
|
||||
|
||||
extension ModifiedContent: EnvironmentReader where Modifier: EnvironmentReader {
|
||||
mutating func setContent(from values: EnvironmentValues) {
|
||||
modifier.setContent(from: values)
|
||||
extension ModifiedContent: _EnvironmentReader where Modifier: _EnvironmentReader {
|
||||
public mutating func _setContent(from values: EnvironmentValues) {
|
||||
modifier._setContent(from: values)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ public struct _BackgroundLayout<Content, Background>: _PrimitiveView
|
|||
}
|
||||
}
|
||||
|
||||
public struct _BackgroundModifier<Background>: ViewModifier, EnvironmentReader
|
||||
public struct _BackgroundModifier<Background>: ViewModifier, _EnvironmentReader
|
||||
where Background: View
|
||||
{
|
||||
public var environment: EnvironmentValues!
|
||||
|
@ -58,7 +58,7 @@ public struct _BackgroundModifier<Background>: ViewModifier, EnvironmentReader
|
|||
)
|
||||
}
|
||||
|
||||
mutating func setContent(from values: EnvironmentValues) {
|
||||
public mutating func _setContent(from values: EnvironmentValues) {
|
||||
environment = values
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ public extension View {
|
|||
}
|
||||
|
||||
@frozen
|
||||
public struct _BackgroundShapeModifier<Style, Bounds>: ViewModifier, EnvironmentReader
|
||||
public struct _BackgroundShapeModifier<Style, Bounds>: ViewModifier, _EnvironmentReader
|
||||
where Style: ShapeStyle, Bounds: Shape
|
||||
{
|
||||
public var environment: EnvironmentValues!
|
||||
|
@ -111,7 +111,7 @@ public struct _BackgroundShapeModifier<Style, Bounds>: ViewModifier, Environment
|
|||
.background(shape.fill(style, style: fillStyle))
|
||||
}
|
||||
|
||||
public mutating func setContent(from values: EnvironmentValues) {
|
||||
public mutating func _setContent(from values: EnvironmentValues) {
|
||||
environment = values
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ public struct _OverlayLayout<Content, Overlay>: _PrimitiveView
|
|||
}
|
||||
}
|
||||
|
||||
public struct _OverlayModifier<Overlay>: ViewModifier, EnvironmentReader
|
||||
public struct _OverlayModifier<Overlay>: ViewModifier, _EnvironmentReader
|
||||
where Overlay: View
|
||||
{
|
||||
public var environment: EnvironmentValues!
|
||||
|
@ -169,7 +169,7 @@ public struct _OverlayModifier<Overlay>: ViewModifier, EnvironmentReader
|
|||
)
|
||||
}
|
||||
|
||||
mutating func setContent(from values: EnvironmentValues) {
|
||||
public mutating func _setContent(from values: EnvironmentValues) {
|
||||
environment = values
|
||||
}
|
||||
}
|
||||
|
|
|
@ -246,16 +246,16 @@ extension EnvironmentValues {
|
|||
for dynamicProp in info.properties.filter({ $0.type is DynamicProperty.Type }) {
|
||||
guard let propInfo = typeInfo(of: dynamicProp.type) else { return }
|
||||
var propWrapper = dynamicProp.get(from: element) as! DynamicProperty
|
||||
for prop in propInfo.properties.filter({ $0.type is EnvironmentReader.Type }) {
|
||||
var wrapper = prop.get(from: propWrapper) as! EnvironmentReader
|
||||
wrapper.setContent(from: self)
|
||||
for prop in propInfo.properties.filter({ $0.type is _EnvironmentReader.Type }) {
|
||||
var wrapper = prop.get(from: propWrapper) as! _EnvironmentReader
|
||||
wrapper._setContent(from: self)
|
||||
prop.set(value: wrapper, on: &propWrapper)
|
||||
}
|
||||
dynamicProp.set(value: propWrapper, on: &element)
|
||||
}
|
||||
for prop in info.properties.filter({ $0.type is EnvironmentReader.Type }) {
|
||||
var wrapper = prop.get(from: element) as! EnvironmentReader
|
||||
wrapper.setContent(from: self)
|
||||
for prop in info.properties.filter({ $0.type is _EnvironmentReader.Type }) {
|
||||
var wrapper = prop.get(from: element) as! _EnvironmentReader
|
||||
wrapper._setContent(from: self)
|
||||
prop.set(value: wrapper, on: &element)
|
||||
}
|
||||
// swiftlint:enable force_cast
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public struct ContainerRelativeShape: Shape, EnvironmentReader {
|
||||
public struct ContainerRelativeShape: Shape, _EnvironmentReader {
|
||||
var containerShape: (CGRect, GeometryProxy) -> Path? = { _, _ in nil }
|
||||
|
||||
public func path(in rect: CGRect) -> Path {
|
||||
|
@ -26,7 +26,7 @@ public struct ContainerRelativeShape: Shape, EnvironmentReader {
|
|||
|
||||
public init() {}
|
||||
|
||||
public mutating func setContent(from values: EnvironmentValues) {
|
||||
public mutating func _setContent(from values: EnvironmentValues) {
|
||||
containerShape = values._containerShape
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ public extension View {
|
|||
|
||||
@frozen
|
||||
public struct _BackgroundStyleModifier<Style>: ViewModifier, _EnvironmentModifier,
|
||||
EnvironmentReader
|
||||
_EnvironmentReader
|
||||
where Style: ShapeStyle
|
||||
{
|
||||
public var environment: EnvironmentValues!
|
||||
|
@ -70,7 +70,8 @@ public struct _BackgroundStyleModifier<Style>: ViewModifier, _EnvironmentModifie
|
|||
}
|
||||
|
||||
public typealias Body = Never
|
||||
public mutating func setContent(from values: EnvironmentValues) {
|
||||
|
||||
public mutating func _setContent(from values: EnvironmentValues) {
|
||||
environment = values
|
||||
}
|
||||
|
||||
|
|
|
@ -31,12 +31,15 @@ import Foundation
|
|||
/// .bold()
|
||||
/// .italic()
|
||||
/// .underline(true, color: .red)
|
||||
public struct Text: _PrimitiveView, Equatable {
|
||||
public struct Text: _PrimitiveView, Equatable, _EnvironmentReader {
|
||||
let storage: _Storage
|
||||
let modifiers: [_Modifier]
|
||||
|
||||
@Environment(\.self)
|
||||
var environment
|
||||
public var environment: EnvironmentValues = .init()
|
||||
|
||||
public mutating func _setContent(from values: EnvironmentValues) {
|
||||
environment = values
|
||||
}
|
||||
|
||||
public static func == (lhs: Text, rhs: Text) -> Bool {
|
||||
lhs.storage == rhs.storage
|
||||
|
|
|
@ -83,6 +83,7 @@ public struct _TextFieldProxy<Label: View> {
|
|||
public var onCommit: () -> () { subject.onCommit }
|
||||
public var onEditingChanged: (Bool) -> () { subject.onEditingChanged }
|
||||
public var textFieldStyle: _AnyTextFieldStyle { subject.environment.textFieldStyle }
|
||||
public var environment: EnvironmentValues { subject.environment }
|
||||
public var foregroundColor: AnyColorBox.ResolvedValue? {
|
||||
guard let foregroundColor = subject.environment.foregroundColor else {
|
||||
return nil
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
import JavaScriptKit
|
||||
import TokamakCore
|
||||
|
||||
@_spi(TokamakStaticHTML)
|
||||
import TokamakStaticHTML
|
||||
|
||||
public typealias HTML = TokamakStaticHTML.HTML
|
||||
|
@ -32,6 +34,7 @@ public struct DynamicHTML<Content>: View, AnyDynamicHTML {
|
|||
public let attributes: [HTMLAttribute: String]
|
||||
public let listeners: [String: Listener]
|
||||
let content: Content
|
||||
let visitContent: (ViewVisitor) -> ()
|
||||
|
||||
fileprivate let cachedInnerHTML: String?
|
||||
|
||||
|
@ -43,6 +46,10 @@ public struct DynamicHTML<Content>: View, AnyDynamicHTML {
|
|||
public var body: Never {
|
||||
neverBody("HTML")
|
||||
}
|
||||
|
||||
public func _visitChildren<V>(_ visitor: V) where V: ViewVisitor {
|
||||
visitContent(visitor)
|
||||
}
|
||||
}
|
||||
|
||||
public extension DynamicHTML where Content: StringProtocol {
|
||||
|
@ -57,6 +64,7 @@ public extension DynamicHTML where Content: StringProtocol {
|
|||
self.listeners = listeners
|
||||
self.content = content
|
||||
cachedInnerHTML = String(content)
|
||||
visitContent = { _ in }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,13 +73,14 @@ extension DynamicHTML: ParentView where Content: View {
|
|||
_ tag: String,
|
||||
_ attributes: [HTMLAttribute: String] = [:],
|
||||
listeners: [String: Listener] = [:],
|
||||
@ViewBuilder content: () -> Content
|
||||
@ViewBuilder content: @escaping () -> Content
|
||||
) {
|
||||
self.tag = tag
|
||||
self.attributes = attributes
|
||||
self.listeners = listeners
|
||||
self.content = content()
|
||||
cachedInnerHTML = nil
|
||||
visitContent = { $0.visit(content()) }
|
||||
}
|
||||
|
||||
@_spi(TokamakCore)
|
||||
|
@ -89,3 +98,10 @@ public extension DynamicHTML where Content == EmptyView {
|
|||
self = DynamicHTML(tag, attributes, listeners: listeners) { EmptyView() }
|
||||
}
|
||||
}
|
||||
|
||||
@_spi(TokamakStaticHTML)
|
||||
extension DynamicHTML: HTMLConvertible {
|
||||
public func attributes(useDynamicLayout: Bool) -> [HTMLAttribute: String] {
|
||||
attributes
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,12 @@
|
|||
// Created by Jed Fox on 06/28/2020.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@_spi(TokamakCore)
|
||||
import TokamakCore
|
||||
|
||||
@_spi(TokamakStaticHTML)
|
||||
import TokamakStaticHTML
|
||||
|
||||
extension TextField: DOMPrimitive where Label == Text {
|
||||
|
@ -39,19 +44,18 @@ extension TextField: DOMPrimitive where Label == Text {
|
|||
}
|
||||
}
|
||||
|
||||
var renderedBody: AnyView {
|
||||
var attributes: [HTMLAttribute: String] {
|
||||
let proxy = _TextFieldProxy(self)
|
||||
|
||||
return AnyView(DynamicHTML("input", [
|
||||
return [
|
||||
"type": proxy.textFieldStyle is RoundedBorderTextFieldStyle ? "search" : "text",
|
||||
.value: proxy.textBinding.wrappedValue,
|
||||
"placeholder": _TextProxy(proxy.label).rawText,
|
||||
"style": """
|
||||
\(css(for: proxy.textFieldStyle)) \
|
||||
\(proxy.foregroundColor.map { "color: \($0.cssValue);" } ?? "")
|
||||
""",
|
||||
"class": className(for: proxy.textFieldStyle),
|
||||
], listeners: [
|
||||
]
|
||||
}
|
||||
|
||||
var listeners: [String: Listener] {
|
||||
let proxy = _TextFieldProxy(self)
|
||||
return [
|
||||
"focus": { _ in proxy.onEditingChanged(true) },
|
||||
"blur": { _ in proxy.onEditingChanged(false) },
|
||||
"keypress": { event in if event.key == "Enter" { proxy.onCommit() } },
|
||||
|
@ -60,6 +64,75 @@ extension TextField: DOMPrimitive where Label == Text {
|
|||
proxy.textBinding.wrappedValue = newValue
|
||||
}
|
||||
},
|
||||
]))
|
||||
]
|
||||
}
|
||||
|
||||
var renderedBody: AnyView {
|
||||
let proxy = _TextFieldProxy(self)
|
||||
return AnyView(DynamicHTML(
|
||||
"input",
|
||||
attributes.merging([
|
||||
"style": """
|
||||
\(css(for: proxy.textFieldStyle)) \
|
||||
\(proxy.foregroundColor.map { "color: \($0.cssValue);" } ?? "")
|
||||
""",
|
||||
"class": className(for: proxy.textFieldStyle),
|
||||
], uniquingKeysWith: { $1 }),
|
||||
listeners: listeners
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@_spi(TokamakStaticHTML)
|
||||
extension TextField: HTMLConvertible, DOMNodeConvertible, Layout, _AnyLayout, Animatable
|
||||
where Label == Text
|
||||
{
|
||||
public typealias AnimatableData = EmptyAnimatableData
|
||||
public var tag: String { "input" }
|
||||
public func attributes(useDynamicLayout: Bool) -> [HTMLAttribute: String] {
|
||||
if useDynamicLayout {
|
||||
return attributes
|
||||
.merging(["style": "padding: 0; border: none;"], uniquingKeysWith: { $0 + $1 })
|
||||
} else {
|
||||
return attributes
|
||||
}
|
||||
}
|
||||
|
||||
public func sizeThatFits(
|
||||
proposal: ProposedViewSize,
|
||||
subviews: Subviews,
|
||||
cache: inout ()
|
||||
) -> CGSize {
|
||||
let proxy = _TextFieldProxy(self)
|
||||
var content = Text(proxy.textBinding.wrappedValue)
|
||||
content._setContent(from: proxy.environment)
|
||||
let contentSize = proxy.environment.measureText(
|
||||
content,
|
||||
proposal,
|
||||
proxy.environment
|
||||
)
|
||||
var label = proxy.label
|
||||
label._setContent(from: proxy.environment)
|
||||
let labelSize = proxy.environment.measureText(
|
||||
label,
|
||||
proposal,
|
||||
proxy.environment
|
||||
)
|
||||
let proposal = proposal.replacingUnspecifiedDimensions()
|
||||
return .init(
|
||||
width: max(proposal.width, max(contentSize.width, labelSize.width)),
|
||||
height: max(contentSize.height, labelSize.height)
|
||||
)
|
||||
}
|
||||
|
||||
public func placeSubviews(
|
||||
in bounds: CGRect,
|
||||
proposal: ProposedViewSize,
|
||||
subviews: Subviews,
|
||||
cache: inout ()
|
||||
) {
|
||||
for subview in subviews {
|
||||
subview.place(at: bounds.origin, proposal: proposal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue