Allow @Environment on all MountedViews (#146)

This commit is contained in:
Carson Katri 2020-07-02 09:36:16 -04:00 committed by GitHub
parent 1632db77b9
commit 525cefe6bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 59 additions and 40 deletions

View File

@ -15,6 +15,8 @@
// Created by Max Desiatov on 28/11/2018. // Created by Max Desiatov on 28/11/2018.
// //
import Runtime
public class MountedView<R: Renderer> { public class MountedView<R: Renderer> {
public internal(set) var view: AnyView public internal(set) var view: AnyView
@ -39,13 +41,46 @@ extension View {
func makeMountedView<R: Renderer>(_ parentTarget: R.TargetType, func makeMountedView<R: Renderer>(_ parentTarget: R.TargetType,
_ environmentValues: EnvironmentValues) _ environmentValues: EnvironmentValues)
-> MountedView<R> { -> MountedView<R> {
let anyView = self as? AnyView ?? AnyView(self) // Find Environment changes
var modifiedEnv = environmentValues
var injectableView = self
if let any = injectableView as? AnyView {
// swiftlint:disable force_try
// Extract the view from the AnyView for modification
var extractedView = any.view
let viewInfo = try! typeInfo(of: any.type)
if viewInfo
.genericTypes
.filter({ $0 is EnvironmentModifier.Type }).count > 0 {
// Apply Environment changes:
if let modifier = try? viewInfo
.property(named: "modifier")
.get(from: any.view) as? EnvironmentModifier {
modifier.modifyEnvironment(&modifiedEnv)
}
}
// Inject @Environment values
// In the future we can also inject @EnvironmentObject values
for prop in viewInfo.properties.filter({ $0.type is EnvironmentReader.Type }) {
// swiftlint:disable force_cast
var wrapper = try! prop.get(from: any.view) as! EnvironmentReader
wrapper.setContent(from: modifiedEnv)
try! prop.set(value: wrapper, on: &extractedView)
// swiftlint:enable force_cast
}
// Set the extractedView back on the AnyView after modification
let anyViewInfo = try! typeInfo(of: AnyView.self)
try! anyViewInfo.property(named: "view").set(value: extractedView, on: &injectableView)
// swiftlint:enable force_try
}
let anyView = injectableView as? AnyView ?? AnyView(injectableView)
if anyView.type == EmptyView.self { if anyView.type == EmptyView.self {
return MountedNull(anyView) return MountedNull(anyView)
} else if anyView.bodyType == Never.self && !(anyView.type is ViewDeferredToRenderer.Type) { } else if anyView.bodyType == Never.self && !(anyView.type is ViewDeferredToRenderer.Type) {
return MountedHostView(anyView, parentTarget, environmentValues) return MountedHostView(anyView, parentTarget, modifiedEnv)
} else { } else {
return MountedCompositeView(anyView, parentTarget, environmentValues) return MountedCompositeView(anyView, parentTarget, modifiedEnv)
} }
} }
} }

View File

@ -89,29 +89,6 @@ public final class StackReconciler<R: Renderer> {
try! stateProperty.set(value: state, on: &compositeView.view.view) try! stateProperty.set(value: state, on: &compositeView.view.view)
} }
let viewInfo = try! typeInfo(of: compositeView.view.type)
if viewInfo
.genericTypes
.filter({ $0 is EnvironmentModifier.Type }).count > 0 {
// Apply Environment changes:
if let modifier = try? viewInfo
.property(named: "modifier")
.get(from: compositeView.view.view) as? EnvironmentModifier {
modifier.modifyEnvironment(&compositeView.environmentValues)
}
}
// Inject @Environment values
// In the future we can also inject @EnvironmentObject values
for prop in viewInfo.properties.filter({ $0.type is EnvironmentReader.Type }) {
// swiftlint:disable force_cast
var wrapper = try! prop.get(from: compositeView.view.view) as! EnvironmentReader
wrapper.setContent(from: compositeView.environmentValues)
try! prop.set(value: wrapper, on: &compositeView.view.view)
// swiftlint:enable force_cast
}
// swiftlint:enable force_try
let result = compositeView.view.bodyClosure(compositeView.view.view) let result = compositeView.view.bodyClosure(compositeView.view.view)
return result return result

View File

@ -17,7 +17,9 @@
public struct Text: View { public struct Text: View {
let content: String let content: String
public let _modifiers: [_Modifier] let modifiers: [_Modifier]
@Environment(\.font) var font: Font?
public enum _Modifier: Equatable { public enum _Modifier: Equatable {
case color(Color?) case color(Color?)
@ -34,7 +36,7 @@ public struct Text: View {
init(content: String, modifiers: [_Modifier] = []) { init(content: String, modifiers: [_Modifier] = []) {
self.content = content self.content = content
_modifiers = modifiers self.modifiers = modifiers
} }
public init(verbatim content: String) { public init(verbatim content: String) {
@ -57,46 +59,51 @@ public struct _TextProxy {
public init(_ subject: Text) { self.subject = subject } public init(_ subject: Text) { self.subject = subject }
public var content: String { subject.content } public var content: String { subject.content }
public var modifiers: [Text._Modifier] {
[
.font(subject.font),
] + subject.modifiers
}
} }
public extension Text { public extension Text {
func foregroundColor(_ color: Color?) -> Text { func foregroundColor(_ color: Color?) -> Text {
.init(content: content, modifiers: _modifiers + [.color(color)]) .init(content: content, modifiers: modifiers + [.color(color)])
} }
func font(_ font: Font?) -> Text { func font(_ font: Font?) -> Text {
.init(content: content, modifiers: _modifiers + [.font(font)]) .init(content: content, modifiers: modifiers + [.font(font)])
} }
func fontWeight(_ weight: Font.Weight?) -> Text { func fontWeight(_ weight: Font.Weight?) -> Text {
.init(content: content, modifiers: _modifiers + [.weight(weight)]) .init(content: content, modifiers: modifiers + [.weight(weight)])
} }
func bold() -> Text { func bold() -> Text {
.init(content: content, modifiers: _modifiers + [.weight(.bold)]) .init(content: content, modifiers: modifiers + [.weight(.bold)])
} }
func italic() -> Text { func italic() -> Text {
.init(content: content, modifiers: _modifiers + [.italic]) .init(content: content, modifiers: modifiers + [.italic])
} }
func strikethrough(_ active: Bool = true, color: Color? = nil) -> Text { func strikethrough(_ active: Bool = true, color: Color? = nil) -> Text {
.init(content: content, modifiers: _modifiers + [.strikethrough(active, color)]) .init(content: content, modifiers: modifiers + [.strikethrough(active, color)])
} }
func underline(_ active: Bool = true, color: Color? = nil) -> Text { func underline(_ active: Bool = true, color: Color? = nil) -> Text {
.init(content: content, modifiers: _modifiers + [.underline(active, color)]) .init(content: content, modifiers: modifiers + [.underline(active, color)])
} }
func kerning(_ kerning: CGFloat) -> Text { func kerning(_ kerning: CGFloat) -> Text {
.init(content: content, modifiers: _modifiers + [.kerning(kerning)]) .init(content: content, modifiers: modifiers + [.kerning(kerning)])
} }
func tracking(_ tracking: CGFloat) -> Text { func tracking(_ tracking: CGFloat) -> Text {
.init(content: content, modifiers: _modifiers + [.tracking(tracking)]) .init(content: content, modifiers: modifiers + [.tracking(tracking)])
} }
func baselineOffset(_ baselineOffset: CGFloat) -> Text { func baselineOffset(_ baselineOffset: CGFloat) -> Text {
.init(content: content, modifiers: _modifiers + [.baseline(baselineOffset)]) .init(content: content, modifiers: modifiers + [.baseline(baselineOffset)])
} }
} }

View File

@ -104,7 +104,7 @@ extension Text: AnyHTML {
var baseline: CGFloat? var baseline: CGFloat?
var strikethrough: (Bool, Color?)? var strikethrough: (Bool, Color?)?
var underline: (Bool, Color?)? var underline: (Bool, Color?)?
for modifier in _modifiers { for modifier in _TextProxy(self).modifiers {
switch modifier { switch modifier {
case let .color(_color): case let .color(_color):
color = _color color = _color

View File

@ -50,7 +50,7 @@ let renderer = DOMRenderer(
Spacer() Spacer()
Text("Forced to bottom.") Text("Forced to bottom.")
EnvironmentDemo() EnvironmentDemo()
.font(.system(size: 21)) .font(.system(size: 8))
} }
}, },
div div