Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial NavigationView implementation #130

Merged
merged 38 commits into from
Jul 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
93456b1
Initial NavigationView implementation
j-f1 Jun 30, 2020
a07e90a
Merge branch 'main' into navigation
j-f1 Jul 1, 2020
364651b
Make the _ButtonProxy type more generic
j-f1 Jul 1, 2020
94d523d
Split Navigation.swift files
j-f1 Jul 1, 2020
36f45ad
Move this too
j-f1 Jul 1, 2020
336b1b6
Implement Navigation controls
j-f1 Jul 1, 2020
d4825ea
Merge branch 'main' into navigation
j-f1 Jul 1, 2020
ce22d0f
Update progress.md
j-f1 Jul 2, 2020
8738b41
Merge branch 'main' into navigation
j-f1 Jul 2, 2020
5f829a4
Make NavigationLinks links
j-f1 Jul 2, 2020
f26b46b
Merge branch 'main' into navigation
j-f1 Jul 4, 2020
037f574
Break line
j-f1 Jul 4, 2020
26ed1d6
Update Path.swift
j-f1 Jul 4, 2020
af8f39d
n-th time’s the charm
j-f1 Jul 4, 2020
56ffa41
Update Path.swift
j-f1 Jul 4, 2020
9cc54a1
Merge branch 'main' into navigation
j-f1 Jul 6, 2020
d6df555
Update project.pbxproj
j-f1 Jul 6, 2020
83a9c3f
Fixes
j-f1 Jul 6, 2020
f033575
Hopefully fix build issues
j-f1 Jul 7, 2020
f0ebe8e
Update Navigation.swift
j-f1 Jul 7, 2020
0a4c538
Merge branch 'main' into navigation
j-f1 Jul 16, 2020
4366a66
Improve ColorDemo
j-f1 Jul 16, 2020
c1784c1
Fixes & reverts
j-f1 Jul 16, 2020
ae6f13d
Fix crash
j-f1 Jul 16, 2020
592bd16
Merge branch 'main' into navigation
j-f1 Jul 16, 2020
e36ef3c
Revert "Fix crash"
j-f1 Jul 16, 2020
9bb10a5
Merge branch 'main' into navigation
j-f1 Jul 17, 2020
112fdde
Tweak rendering of demos
j-f1 Jul 20, 2020
80c8ae4
Merge branch 'main' into navigation
j-f1 Jul 20, 2020
d5667c1
add todo for accessibility
j-f1 Jul 20, 2020
73c9c3f
Apply suggestions from @MaxDesiatov
j-f1 Jul 20, 2020
8c2f531
Update TokamakDemo.swift
j-f1 Jul 20, 2020
4498b9f
Move things to Core.swift
j-f1 Jul 20, 2020
e4b60d8
Switch default destination to EmptyView
j-f1 Jul 20, 2020
3721737
Fix build for macOS
j-f1 Jul 20, 2020
538994e
Revert "Apply suggestions from @MaxDesiatov"
j-f1 Jul 20, 2020
edf0374
Merge branch 'main' into navigation
j-f1 Jul 22, 2020
46ab7b9
Merge branch 'main' into navigation
carson-katri Jul 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions NativeDemo/TokamakDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
Expand Down Expand Up @@ -488,6 +489,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.jedfox.Tokamak-Native";
Expand Down Expand Up @@ -556,9 +558,9 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
PRODUCT_NAME = "TokamakDemo Native";
SDKROOT = macosx;
SUPPORTED_PLATFORMS = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
Expand All @@ -580,9 +582,9 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
PRODUCT_NAME = "TokamakDemo Native";
SDKROOT = macosx;
SUPPORTED_PLATFORMS = macosx;
SWIFT_VERSION = 5.0;
};
name = Release;
Expand All @@ -596,6 +598,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
Expand All @@ -610,6 +613,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
Expand Down
7 changes: 4 additions & 3 deletions NativeDemo/UIAppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ public class NSApplication: UIApplication {}

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
let window = UIWindow()
var window: UIWindow?
func application(
_: UIApplication,
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
window.rootViewController = UIHostingController(rootView: TokamakDemoView())
window.makeKeyAndVisible()
window = UIWindow()
window?.rootViewController = UIHostingController(rootView: TokamakDemoView())
window?.makeKeyAndVisible()
return true
}
}
25 changes: 25 additions & 0 deletions Sources/TokamakCore/Modifiers/Navigation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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.

extension View {
// FIXME: Implement
public func navigationBarTitle<S>(_ title: S) -> some View where S: StringProtocol {
self
}

// FIXME: Implement
public func navigationTitle<S>(_ title: S) -> some View where S: StringProtocol {
self
}
}
10 changes: 6 additions & 4 deletions Sources/TokamakCore/Views/Buttons/Button.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,13 @@ extension Button: ParentView {
}

/// This is a helper class that works around absence of "package private" access control in Swift
public struct _ButtonProxy {
public let subject: Button<Text>
public struct _ButtonProxy<Label> where Label: View {
let subject: Button<Label>

public init(_ subject: Button<Text>) { self.subject = subject }
public init(_ subject: Button<Label>) { self.subject = subject }
public var action: () -> () { subject.action }
}

extension _ButtonProxy where Label == Text {
public var label: _TextProxy { _TextProxy(subject.label) }
public var action: () -> () { subject.action }
}
79 changes: 79 additions & 0 deletions Sources/TokamakCore/Views/NavigationLink.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// 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.
//
// Created by Jed Fox on 06/30/2020.
//

public struct NavigationLink<Label, Destination>: View where Label: View, Destination: View {
let destination: Destination
let label: Label

@Environment(_navigationDestinationKey) var navigationContext

public init(destination: Destination, @ViewBuilder label: () -> Label) {
self.destination = destination
self.label = label()
}

/// Creates an instance that presents `destination` when active.
// public init(destination: Destination, isActive: Binding<Bool>, @ViewBuilder label: () -> Label)

/// Creates an instance that presents `destination` when `selection` is set
/// to `tag`.
// public init<V>(
// destination: Destination,
// tag: V, selection: Binding<V?>,
// @ViewBuilder label: () -> Label
// ) where V : Hashable

public var body: Never {
neverBody("NavigationLink")
}
}

extension NavigationLink where Label == Text {
/// Creates an instance that presents `destination`, with a `Text` label
/// generated from a title string.
public init<S>(_ title: S, destination: Destination) where S: StringProtocol {
self.destination = destination
label = Text(title)
}

/// Creates an instance that presents `destination` when active, with a
/// `Text` label generated from a title string.
// public init<S>(
// _ title: S, destination: Destination,
// isActive: Binding<Bool>
// ) where S : StringProtocol

/// Creates an instance that presents `destination` when `selection` is set
/// to `tag`, with a `Text` label generated from a title string.
// public init<S, V>(
// _ title: S, destination: Destination,
// tag: V, selection: Binding<V?>
// ) where S : StringProtocol, V : Hashable
}

/// This is a helper class that works around absence of "package private" access control in Swift
public struct _NavigationLinkProxy<Label, Destination> where Label: View, Destination: View {
public let subject: NavigationLink<Label, Destination>

public init(_ subject: NavigationLink<Label, Destination>) { self.subject = subject }

public var label: Label { subject.label }

public func activate() {
subject.navigationContext!.wrappedValue = AnyView(subject.destination)
}
}
62 changes: 62 additions & 0 deletions Sources/TokamakCore/Views/NavigationView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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.
//
// Created by Jed Fox on 06/30/2020.
//

public struct NavigationView<Content>: View where Content: View {
let content: Content

@State var destination = AnyView(EmptyView())

public init(@ViewBuilder content: () -> Content) {
self.content = content()
}

public var body: Never {
neverBody("NavigationView")
}
}

/// This is a helper class that works around absence of "package private" access control in Swift
public struct _NavigationViewProxy<Content: View> {
public let subject: NavigationView<Content>

public init(_ subject: NavigationView<Content>) { self.subject = subject }

public var content: Content { subject.content }
public var body: some View {
HStack {
content
subject.destination
}.environment(\.navigationDestination, subject.$destination)
}
}

struct NavigationDestinationKey: EnvironmentKey {
public static let defaultValue: Binding<AnyView>? = nil
}

extension EnvironmentValues {
var navigationDestination: Binding<AnyView>? {
get {
self[NavigationDestinationKey.self]
}
set {
self[NavigationDestinationKey.self] = newValue
}
}
}

public let _navigationDestinationKey = \EnvironmentValues.navigationDestination
4 changes: 4 additions & 0 deletions Sources/TokamakDOM/Core.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ public typealias HStack = TokamakCore.HStack
public typealias LazyHGrid = TokamakCore.LazyHGrid
public typealias LazyVGrid = TokamakCore.LazyVGrid
public typealias List = TokamakCore.List
public typealias NavigationLink = TokamakCore.NavigationLink
public typealias NavigationView = TokamakCore.NavigationView
public typealias OutlineGroup = TokamakCore.OutlineGroup
public typealias Picker = TokamakCore.Picker
public typealias ScrollView = TokamakCore.ScrollView
Expand Down Expand Up @@ -112,6 +114,8 @@ public typealias SceneStorage = TokamakCore.SceneStorage

// MARK: Misc

public typealias ViewBuilder = TokamakCore.ViewBuilder

// FIXME: I would put this inside TokamakCore, but for
// some reason it doesn't get exported with the typealias
extension Text {
Expand Down
1 change: 0 additions & 1 deletion Sources/TokamakDOM/Shapes/Path.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
//
// Created by Carson Katri on 6/29/20.
//

import TokamakCore

extension Path: ViewDeferredToRenderer {
Expand Down
30 changes: 30 additions & 0 deletions Sources/TokamakDOM/Views/NavigationLink.swift
Original file line number Diff line number Diff line change
@@ -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 TokamakCore

extension NavigationLink: ViewDeferredToRenderer {
public var deferredBody: AnyView {
let proxy = _NavigationLinkProxy(self)
return AnyView(
HTML("a", [
"href": "javascript:void%200",
], listeners: [
// FIXME: Focus destination or something so assistive
// technology knows where to look when clicking the link.
"click": { _ in proxy.activate() },
]) { proxy.label }
)
}
}
29 changes: 29 additions & 0 deletions Sources/TokamakDOM/Views/NavigationView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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 NavigationView: ViewDeferredToRenderer {
public var deferredBody: AnyView {
AnyView(HTML("div", [
"style": """
display: flex; flex-direction: row; align-items: stretch;
width: 100%; height: 100%;
""",
]) {
_NavigationViewProxy(self).body
})
}
}
42 changes: 22 additions & 20 deletions Sources/TokamakDemo/ColorDemo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,28 +59,30 @@ public struct ColorDemo: View {
@State private var v2: String = "0.5"

public var body: some View {
VStack {
Button("Input \(colorForm.rawValue.uppercased())") {
colorForm = colorForm == .rgb ? .hsb : .rgb
}
TextField(colorForm == .rgb ? "Red" : "Hue", text: $v0)
TextField(colorForm == .rgb ? "Green" : "Saturation", text: $v1)
TextField(colorForm == .rgb ? "Blue" : "Brightness", text: $v2)
Text("\(v0) \(v1) \(v2)")
.bold()
.padding()
.background(color)
Text("Accent Color: \(Color.accentColor.description)")
.bold()
.padding()
.background(Color.accentColor)
ForEach(colors, id: \.self) {
Text($0.description)
.font(.caption)
ScrollView {
VStack {
Button("Input \(colorForm.rawValue.uppercased())") {
colorForm = colorForm == .rgb ? .hsb : .rgb
}
TextField(colorForm == .rgb ? "Red" : "Hue", text: $v0)
TextField(colorForm == .rgb ? "Green" : "Saturation", text: $v1)
TextField(colorForm == .rgb ? "Blue" : "Brightness", text: $v2)
Text("\(v0) \(v1) \(v2)")
.bold()
.padding()
.background($0)
}
.background(color)
Text("Accent Color: \(Color.accentColor.description)")
.bold()
.padding()
.background(Color.accentColor)
ForEach(colors, id: \.self) {
Text($0.description)
.font(.caption)
.bold()
.padding()
.background($0)
}
}.padding(.horizontal)
}
}
}
Loading