Merge branch main
This commit is contained in:
commit
4b05fd8523
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
protocol AppearanceActionProtocol {
|
protocol AppearanceActionType {
|
||||||
var appear: (() -> ())? { get }
|
var appear: (() -> ())? { get }
|
||||||
var disappear: (() -> ())? { get }
|
var disappear: (() -> ())? { get }
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ struct _AppearanceActionModifier: ViewModifier {
|
||||||
typealias Body = Never
|
typealias Body = Never
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ModifiedContent: AppearanceActionProtocol
|
extension ModifiedContent: AppearanceActionType
|
||||||
where Content: View, Modifier == _AppearanceActionModifier {
|
where Content: View, Modifier == _AppearanceActionModifier {
|
||||||
var appear: (() -> ())? { modifier.appear }
|
var appear: (() -> ())? { modifier.appear }
|
||||||
var disappear: (() -> ())? { modifier.disappear }
|
var disappear: (() -> ())? { modifier.disappear }
|
||||||
|
|
|
@ -43,7 +43,10 @@ extension _BackgroundModifier: Equatable where Background: Equatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension View {
|
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))
|
modifier(_BackgroundModifier(background: background, alignment: alignment))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,19 +22,34 @@ final class MountedCompositeView<R: Renderer>: MountedCompositeElement<R> {
|
||||||
override func mount(with reconciler: StackReconciler<R>) {
|
override func mount(with reconciler: StackReconciler<R>) {
|
||||||
let childBody = reconciler.render(compositeView: self)
|
let childBody = reconciler.render(compositeView: self)
|
||||||
|
|
||||||
if let appearanceAction = view.view as? AppearanceActionProtocol {
|
if let appearanceAction = view.view as? AppearanceActionType {
|
||||||
appearanceAction.appear?()
|
appearanceAction.appear?()
|
||||||
}
|
}
|
||||||
|
|
||||||
let child: MountedElement<R> = childBody.makeMountedView(parentTarget, environmentValues)
|
let child: MountedElement<R> = childBody.makeMountedView(parentTarget, environmentValues)
|
||||||
mountedChildren = [child]
|
mountedChildren = [child]
|
||||||
child.mount(with: reconciler)
|
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>) {
|
override func unmount(with reconciler: StackReconciler<R>) {
|
||||||
mountedChildren.forEach { $0.unmount(with: reconciler) }
|
mountedChildren.forEach { $0.unmount(with: reconciler) }
|
||||||
|
|
||||||
if let appearanceAction = view.view as? AppearanceActionProtocol {
|
if let appearanceAction = view.view as? AppearanceActionType {
|
||||||
appearanceAction.disappear?()
|
appearanceAction.disappear?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ public final class MountedHostView<R: Renderer>: MountedElement<R> {
|
||||||
private let parentTarget: R.TargetType
|
private let parentTarget: R.TargetType
|
||||||
|
|
||||||
/// Target of this host view supplied by a renderer after mounting has completed.
|
/// 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) {
|
init(_ view: AnyView, _ parentTarget: R.TargetType, _ environmentValues: EnvironmentValues) {
|
||||||
self.parentTarget = parentTarget
|
self.parentTarget = parentTarget
|
||||||
|
|
|
@ -83,7 +83,11 @@ extension Shape {
|
||||||
OffsetShape(shape: self, offset: .init(width: x, height: y))
|
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,
|
ScaledShape(shape: self,
|
||||||
scale: CGSize(width: x, height: y), anchor: anchor)
|
scale: CGSize(width: x, height: y), anchor: anchor)
|
||||||
}
|
}
|
||||||
|
@ -122,7 +126,10 @@ extension Shape {
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
_ShapeView(shape: self, style: content, fillStyle: style)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,24 +16,35 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
extension InsettableShape {
|
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)
|
inset(by: style.lineWidth / 2)
|
||||||
.stroke(style: style)
|
.stroke(style: style)
|
||||||
.fill(content, style: FillStyle(antialiased: antialiased))
|
.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)
|
inset(by: style.lineWidth / 2)
|
||||||
.stroke(style: style)
|
.stroke(style: style)
|
||||||
.fill(style: FillStyle(antialiased: antialiased))
|
.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),
|
strokeBorder(content, style: StrokeStyle(lineWidth: lineWidth),
|
||||||
antialiased: antialiased)
|
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),
|
strokeBorder(style: StrokeStyle(lineWidth: lineWidth),
|
||||||
antialiased: antialiased)
|
antialiased: antialiased)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,10 +48,7 @@ public struct EdgeInsets: Equatable {
|
||||||
public var bottom: CGFloat
|
public var bottom: CGFloat
|
||||||
public var trailing: CGFloat
|
public var trailing: CGFloat
|
||||||
|
|
||||||
public init(top: CGFloat,
|
public init(top: CGFloat, leading: CGFloat, bottom: CGFloat, trailing: CGFloat) {
|
||||||
leading: CGFloat,
|
|
||||||
bottom: CGFloat,
|
|
||||||
trailing: CGFloat) {
|
|
||||||
self.top = top
|
self.top = top
|
||||||
self.leading = leading
|
self.leading = leading
|
||||||
self.bottom = bottom
|
self.bottom = bottom
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -63,7 +63,7 @@ let document = global.document.object!
|
||||||
let body = document.body.object!
|
let body = document.body.object!
|
||||||
let head = document.head.object!
|
let head = document.head.object!
|
||||||
|
|
||||||
let timeoutScheduler = { (closure: @escaping () -> ()) in
|
private let timeoutScheduler = { (closure: @escaping () -> ()) in
|
||||||
let fn = JSClosure { _ in
|
let fn = JSClosure { _ in
|
||||||
closure()
|
closure()
|
||||||
return .undefined
|
return .undefined
|
||||||
|
|
|
@ -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
|
|
@ -95,39 +95,46 @@ var redactDemo: NavItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var links: [NavItem] {
|
var domRefDemo: NavItem {
|
||||||
[
|
#if os(WASI)
|
||||||
NavItem("Counter", destination:
|
return NavItem("DOM reference", destination: DOMRefDemo())
|
||||||
Counter(count: Count(value: 5), limit: 15)
|
#else
|
||||||
.padding()
|
return NavItem(unavailable: "DOM reference")
|
||||||
.background(Color(red: 0.9, green: 0.9, blue: 0.9, opacity: 1.0))
|
#endif
|
||||||
.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,
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
struct TokamakDemoView: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView { () -> AnyView in
|
NavigationView { () -> AnyView in
|
||||||
|
|
Loading…
Reference in New Issue