Merge branch main

This commit is contained in:
Carson Katri 2020-08-03 17:08:57 -04:00
commit 4b05fd8523
13 changed files with 185 additions and 48 deletions

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
protocol AppearanceActionProtocol {
protocol AppearanceActionType {
var appear: (() -> ())? { get }
var disappear: (() -> ())? { get }
}
@ -29,7 +29,7 @@ struct _AppearanceActionModifier: ViewModifier {
typealias Body = Never
}
extension ModifiedContent: AppearanceActionProtocol
extension ModifiedContent: AppearanceActionType
where Content: View, Modifier == _AppearanceActionModifier {
var appear: (() -> ())? { modifier.appear }
var disappear: (() -> ())? { modifier.disappear }

View File

@ -43,7 +43,10 @@ extension _BackgroundModifier: Equatable where Background: Equatable {
}
extension View {
public func background<Background>(_ background: Background, alignment: Alignment = .center) -> some View where Background: View {
public func background<Background>(
_ background: Background,
alignment: Alignment = .center
) -> some View where Background: View {
modifier(_BackgroundModifier(background: background, alignment: alignment))
}
}

View File

@ -22,19 +22,34 @@ final class MountedCompositeView<R: Renderer>: MountedCompositeElement<R> {
override func mount(with reconciler: StackReconciler<R>) {
let childBody = reconciler.render(compositeView: self)
if let appearanceAction = view.view as? AppearanceActionProtocol {
if let appearanceAction = view.view as? AppearanceActionType {
appearanceAction.appear?()
}
let child: MountedElement<R> = childBody.makeMountedView(parentTarget, environmentValues)
mountedChildren = [child]
child.mount(with: reconciler)
// `_TargetRef` is a composite view, so it's enough to check for it only here
if var targetRef = view.view as? TargetRefType {
// `_TargetRef` body is not always a host view that has a target, need to traverse
// all descendants to find a `MountedHostView<R>` instance.
var descendant: MountedElement<R>? = child
while descendant != nil && !(descendant is MountedHostView<R>) {
descendant = descendant?.mountedChildren.first
}
guard let hostDescendant = descendant as? MountedHostView<R> else { return }
targetRef.target = hostDescendant.target
view.view = targetRef
}
}
override func unmount(with reconciler: StackReconciler<R>) {
mountedChildren.forEach { $0.unmount(with: reconciler) }
if let appearanceAction = view.view as? AppearanceActionProtocol {
if let appearanceAction = view.view as? AppearanceActionType {
appearanceAction.disappear?()
}
}

View File

@ -29,7 +29,7 @@ public final class MountedHostView<R: Renderer>: MountedElement<R> {
private let parentTarget: R.TargetType
/// Target of this host view supplied by a renderer after mounting has completed.
private var target: R.TargetType?
private(set) var target: R.TargetType?
init(_ view: AnyView, _ parentTarget: R.TargetType, _ environmentValues: EnvironmentValues) {
self.parentTarget = parentTarget

View File

@ -83,7 +83,11 @@ extension Shape {
OffsetShape(shape: self, offset: .init(width: x, height: y))
}
public func scale(x: CGFloat = 1, y: CGFloat = 1, anchor: UnitPoint = .center) -> ScaledShape<Self> {
public func scale(
x: CGFloat = 1,
y: CGFloat = 1,
anchor: UnitPoint = .center
) -> ScaledShape<Self> {
ScaledShape(shape: self,
scale: CGSize(width: x, height: y), anchor: anchor)
}
@ -122,7 +126,10 @@ extension Shape {
}
extension Shape {
public func fill<S>(_ content: S, style: FillStyle = FillStyle()) -> some View where S: ShapeStyle {
public func fill<S>(
_ content: S,
style: FillStyle = FillStyle()
) -> some View where S: ShapeStyle {
_ShapeView(shape: self, style: content, fillStyle: style)
}

View File

@ -16,24 +16,35 @@
//
extension InsettableShape {
public func strokeBorder<S>(_ content: S, style: StrokeStyle, antialiased: Bool = true) -> some View where S: ShapeStyle {
public func strokeBorder<S>(
_ content: S,
style: StrokeStyle,
antialiased: Bool = true
) -> some View where S: ShapeStyle {
inset(by: style.lineWidth / 2)
.stroke(style: style)
.fill(content, style: FillStyle(antialiased: antialiased))
}
@inlinable public func strokeBorder(style: StrokeStyle, antialiased: Bool = true) -> some View {
@inlinable
public func strokeBorder(style: StrokeStyle, antialiased: Bool = true) -> some View {
inset(by: style.lineWidth / 2)
.stroke(style: style)
.fill(style: FillStyle(antialiased: antialiased))
}
@inlinable public func strokeBorder<S>(_ content: S, lineWidth: CGFloat = 1, antialiased: Bool = true) -> some View where S: ShapeStyle {
@inlinable
public func strokeBorder<S>(
_ content: S,
lineWidth: CGFloat = 1,
antialiased: Bool = true
) -> some View where S: ShapeStyle {
strokeBorder(content, style: StrokeStyle(lineWidth: lineWidth),
antialiased: antialiased)
}
@inlinable public func strokeBorder(lineWidth: CGFloat = 1, antialiased: Bool = true) -> some View {
@inlinable
public func strokeBorder(lineWidth: CGFloat = 1, antialiased: Bool = true) -> some View {
strokeBorder(style: StrokeStyle(lineWidth: lineWidth),
antialiased: antialiased)
}

View File

@ -0,0 +1,39 @@
// Copyright 2020 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
protocol TargetRefType {
var target: Target? { get set }
}
public struct _TargetRef<V: View, T>: View, TargetRefType {
let binding: Binding<T?>
let view: V
var target: Target? {
get { binding.wrappedValue as? Target }
set { binding.wrappedValue = newValue as? T }
}
public var body: V { view }
}
extension View {
/** Allows capturing target instance of aclosest descendant host view. The resulting instance
is written to a given `binding`. */
public func _targetRef<T: Target>(_ binding: Binding<T?>) -> _TargetRef<Self, T> {
.init(binding: binding, view: self)
}
}

View File

@ -48,10 +48,7 @@ public struct EdgeInsets: Equatable {
public var bottom: CGFloat
public var trailing: CGFloat
public init(top: CGFloat,
leading: CGFloat,
bottom: CGFloat,
trailing: CGFloat) {
public init(top: CGFloat, leading: CGFloat, bottom: CGFloat, trailing: CGFloat) {
self.top = top
self.leading = leading
self.bottom = bottom

View File

@ -0,0 +1,30 @@
// Copyright 2020 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import JavaScriptKit
import TokamakCore
extension View {
/** Allows capturing DOM references of host views. The resulting reference is written
to a given `binding`.
*/
public func _domRef(_ binding: Binding<JSObjectRef?>) -> some View {
// Convert `Binding<JSObjectRef?>` to `Binding<DOMNode?>` first.
let targetBinding = Binding(
get: { binding.wrappedValue.map(DOMNode.init) },
set: { binding.wrappedValue = $0?.ref }
)
return _targetRef(targetBinding)
}
}

View File

@ -63,7 +63,7 @@ let document = global.document.object!
let body = document.body.object!
let head = document.head.object!
let timeoutScheduler = { (closure: @escaping () -> ()) in
private let timeoutScheduler = { (closure: @escaping () -> ()) in
let fn = JSClosure { _ in
closure()
return .undefined

View File

@ -0,0 +1,28 @@
// Copyright 2020 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if os(WASI)
import JavaScriptKit
import TokamakShim
struct DOMRefDemo: View {
@State var button: JSObjectRef?
var body: some View {
Button("Click me") {
button?.innerHTML = "This text was set directly through a DOM reference"
}._domRef($button)
}
}
#endif

View File

@ -95,39 +95,46 @@ var redactDemo: NavItem {
}
}
var links: [NavItem] {
[
NavItem("Counter", destination:
Counter(count: Count(value: 5), limit: 15)
.padding()
.background(Color(red: 0.9, green: 0.9, blue: 0.9, opacity: 1.0))
.border(Color.red, width: 3)
.foregroundColor(.black)),
NavItem("ZStack", destination: ZStack {
Text("I'm on bottom")
Text("I'm forced to the top")
.zIndex(1)
Text("I'm on top")
}.padding(20)),
NavItem("ButtonStyle", destination: ButtonStyleDemo()),
NavItem("ForEach", destination: ForEachDemo()),
NavItem("Text", destination: TextDemo()),
NavItem("Toggle", destination: ToggleDemo()),
NavItem("Path", destination: PathDemo()),
NavItem("TextField", destination: TextFieldDemo()),
NavItem("Spacer", destination: SpacerDemo()),
NavItem("Environment", destination: EnvironmentDemo().font(.system(size: 8))),
NavItem("Picker", destination: PickerDemo()),
NavItem("List", destination: listDemo),
sidebarDemo,
outlineGroupDemo,
NavItem("Color", destination: ColorDemo()),
appStorageDemo,
gridDemo,
redactDemo,
]
var domRefDemo: NavItem {
#if os(WASI)
return NavItem("DOM reference", destination: DOMRefDemo())
#else
return NavItem(unavailable: "DOM reference")
#endif
}
let links = [
NavItem("Counter", destination:
Counter(count: Count(value: 5), limit: 15)
.padding()
.background(Color(red: 0.9, green: 0.9, blue: 0.9, opacity: 1.0))
.border(Color.red, width: 3)
.foregroundColor(.black)),
NavItem("ZStack", destination: ZStack {
Text("I'm on bottom")
Text("I'm forced to the top")
.zIndex(1)
Text("I'm on top")
}.padding(20)),
NavItem("ButtonStyle", destination: ButtonStyleDemo()),
NavItem("ForEach", destination: ForEachDemo()),
NavItem("Text", destination: TextDemo()),
NavItem("Toggle", destination: ToggleDemo()),
NavItem("Path", destination: PathDemo()),
NavItem("TextField", destination: TextFieldDemo()),
NavItem("Spacer", destination: SpacerDemo()),
NavItem("Environment", destination: EnvironmentDemo().font(.system(size: 8))),
NavItem("Picker", destination: PickerDemo()),
NavItem("List", destination: listDemo),
sidebarDemo,
outlineGroupDemo,
NavItem("Color", destination: ColorDemo()),
appStorageDemo,
gridDemo,
redactDemo,
domRefDemo,
]
struct TokamakDemoView: View {
var body: some View {
NavigationView { () -> AnyView in