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.
//
import Runtime
public class MountedView<R: Renderer> {
public internal(set) var view: AnyView
@ -39,13 +41,46 @@ extension View {
func makeMountedView<R: Renderer>(_ parentTarget: R.TargetType,
_ environmentValues: EnvironmentValues)
-> 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 {
return MountedNull(anyView)
} else if anyView.bodyType == Never.self && !(anyView.type is ViewDeferredToRenderer.Type) {
return MountedHostView(anyView, parentTarget, environmentValues)
return MountedHostView(anyView, parentTarget, modifiedEnv)
} 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)
}
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)
return result

View File

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

View File

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