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

Add gestures initial functionality #538

Open
wants to merge 35 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
19a9d8c
Add gestures
Jul 31, 2023
927fded
fix style
Jul 31, 2023
be15819
Add missing type
Jul 31, 2023
fddad3c
fix header comments
Aug 1, 2023
d31f7f3
Address PR feedback + add view data changes reaction modifiers
Aug 9, 2023
1b52c51
quick fix
Aug 9, 2023
6ace747
Add missing onChangedAction for long press
Aug 9, 2023
779c6d3
remove prints
Aug 9, 2023
4ec2d79
gestures standard, simultaneous and highPriority handling
Aug 13, 2023
72269c1
Fix minor tap gesture issue
Aug 15, 2023
5781500
Global listeners to track continuous pointer movement/up events
Aug 15, 2023
b73ae74
Squashed commit of the following:
Aug 19, 2023
eba439f
Address PR feedback
Aug 19, 2023
f6c7227
Merge branch 'shial4/feat/530-add-gestures' into shial4/feat/530-coor…
Aug 19, 2023
8a0a468
Update base branch
Aug 19, 2023
cbf9ef3
some cosmetics
Aug 19, 2023
4bab2f2
Add gesture phase context
Aug 20, 2023
46f2a30
move to separate file
Aug 20, 2023
4682ca5
Remove target.getBoundingClientRect to improve performance
Aug 20, 2023
086a5a1
Fix minor issues
Aug 21, 2023
28757d5
Add global publisher, fix drag issue state
Aug 23, 2023
98915cc
remove print
Aug 23, 2023
a1f4419
Merge pull request #1 from shial4/shial4/feat/530-coordinate-space
shial4 Aug 23, 2023
b60386a
Add Fibre support for gestures
Aug 25, 2023
73d4d71
swap to root
Aug 25, 2023
d89bc06
Add tests & clean up
Aug 26, 2023
2a7485a
Squashed commit of the following:
Aug 26, 2023
37db9e4
fix test
Sep 1, 2023
6a97c98
Remove redundant functionality
Sep 3, 2023
39bb535
run checks
Sep 4, 2023
64af12a
Update change modifiers
Sep 7, 2023
cb0818a
due to the use of Task in the code, we bump min version
Sep 7, 2023
cf86cbf
Remove _onUpdate from on change modifier
Sep 10, 2023
29998d7
Fix change & receive
Sep 15, 2023
1ef34f8
Add gesture tests
Sep 17, 2023
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
2 changes: 1 addition & 1 deletion NativeDemo/NSAppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import Cocoa
import SwiftUI

@NSApplicationMain
@main
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!

Expand Down
2 changes: 1 addition & 1 deletion NativeDemo/UIAppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import UIKit
// so we only need one Info.plist
public class NSApplication: UIApplication {}

@UIApplicationMain
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(
Expand Down
6 changes: 3 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// swift-tools-version:5.6
// swift-tools-version:5.7

Check failure on line 1 in Package.swift

View workflow job for this annotation

GitHub Actions / codecov

The current code coverage percentage is failing with (minimum allowed: %).

import PackageDescription

let package = Package(
name: "Tokamak",
platforms: [
.macOS(.v11),
.iOS(.v13),
.macOS(.v13),
.iOS(.v16),
],
products: [
// Products define the executables and libraries produced by a package,
Expand Down
2 changes: 1 addition & 1 deletion Sources/TokamakCore/Animation/Animatable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public struct AnimatablePair<First, Second>: VectorArithmetic
}

@inlinable
internal subscript() -> (First, Second) {
subscript() -> (First, Second) {
get { (first, second) }
set { (first, second) = newValue }
}
Expand Down
3 changes: 2 additions & 1 deletion Sources/TokamakCore/App/Scenes/Scene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public protocol Scene {
/// You can `visit(_:)` either another `Scene` or a `View` with a `SceneVisitor`
func _visitChildren<V: SceneVisitor>(_ visitor: V)

/// Create `SceneOutputs`, including any modifications to the environment, preferences, or a custom
/// Create `SceneOutputs`, including any modifications to the environment, preferences, or a
/// custom
/// `LayoutComputer` from the `SceneInputs`.
///
/// > At the moment, `SceneInputs`/`SceneOutputs` are identical to `ViewInputs`/`ViewOutputs`.
Expand Down
48 changes: 48 additions & 0 deletions Sources/TokamakCore/CoordinateSpace/CoordinateSpace.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 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 Szymon on 18/8/2023.
//

import Foundation

public enum CoordinateSpace {
case global
case local
case named(AnyHashable)
}

extension CoordinateSpace: Equatable, Hashable {
// Equatable and Hashable conformance
}

public extension CoordinateSpace {
var isGlobal: Bool {
switch self {
case .global:
return true
default:
return false
}
}

var isLocal: Bool {
switch self {
case .local:
return true
default:
return false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 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 Szymon on 19/8/2023.
//

import Foundation

private struct CoordinateSpaceEnvironmentKey: EnvironmentKey {
static let defaultValue: CoordinateSpaceContext = .init()
}

extension EnvironmentValues {
var _coordinateSpace: CoordinateSpaceContext {
get { self[CoordinateSpaceEnvironmentKey.self] }
set { self[CoordinateSpaceEnvironmentKey.self] = newValue }
}
}

class CoordinateSpaceContext {
/// Stores currently active CoordinateSpace against it's origin point in global coordinates
var activeCoordinateSpace: [CoordinateSpace: CGPoint] = [:]
}

extension CoordinateSpace {
static func convertGlobalSpaceCoordinates(
rect: CGRect,
toNamedOrigin namedOrigin: CGPoint
) -> CGRect {
let translatedOrigin = convert(rect.origin, toNamedOrigin: namedOrigin)
return CGRect(origin: translatedOrigin, size: rect.size)
}

static func convert(_ point: CGPoint, toNamedOrigin namedOrigin: CGPoint) -> CGPoint {
CGPoint(x: point.x - namedOrigin.x, y: point.y - namedOrigin.y)
}
}
2 changes: 1 addition & 1 deletion Sources/TokamakCore/Environment/EnvironmentObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public struct EnvironmentObject<ObjectType>: DynamicProperty
{
@dynamicMemberLookup
public struct Wrapper {
internal let root: ObjectType
let root: ObjectType
public subscript<Subject>(
dynamicMember keyPath: ReferenceWritableKeyPath<ObjectType, Subject>
) -> Binding<Subject> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ extension FiberReconciler.Fiber: CustomDebugStringConvertible {
proposal: .unspecified
)
return """
\(spaces)\(String(describing: typeInfo?.type ?? Any.self)
.split(separator: "<")[0])\(element != nil ? "(\(element!))" : "") {\(element != nil ?
\(spaces)\(
String(describing: typeInfo?.type ?? Any.self)
.split(separator: "<")[0]
)\(element != nil ? "(\(element!))" : "") {\(element != nil ?
"\n\(spaces)geometry: \(geometry)" :
"")
\(child?.flush(level: level + 2) ?? "")
Expand Down
4 changes: 2 additions & 2 deletions Sources/TokamakCore/Fiber/FiberReconciler+TreeReducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ extension FiberReconciler {
}

static func reduce<S>(into partialResult: inout Result, nextScene: S) where S: Scene {
Self.reduce(
reduce(
into: &partialResult,
nextValue: nextScene,
createFiber: { scene, element, parent, elementParent, preferenceParent, _, _, reconciler in
Expand All @@ -80,7 +80,7 @@ extension FiberReconciler {
}

static func reduce<V>(into partialResult: inout Result, nextView: V) where V: View {
Self.reduce(
reduce(
into: &partialResult,
nextValue: nextView,
createFiber: {
Expand Down
3 changes: 2 additions & 1 deletion Sources/TokamakCore/Fiber/FiberRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ public protocol FiberRenderer {

/// Run `action` on the next run loop.
///
/// Called by the `FiberReconciler` to perform reconciliation after all changed Fibers are collected.
/// Called by the `FiberReconciler` to perform reconciliation after all changed Fibers are
/// collected.
///
/// For example, take the following sample `View`:
///
Expand Down
4 changes: 2 additions & 2 deletions Sources/TokamakCore/Fiber/Layout/StackLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public extension StackLayout {
/// A `vertical` axis will return `height`.
/// A `horizontal` axis will return `width`.
static var mainAxis: WritableKeyPath<CGSize, CGFloat> {
switch Self.orientation {
switch orientation {
case .vertical: return \.height
case .horizontal: return \.width
}
Expand All @@ -86,7 +86,7 @@ public extension StackLayout {
/// A `vertical` axis will return `width`.
/// A `horizontal` axis will return `height`.
static var crossAxis: WritableKeyPath<CGSize, CGFloat> {
switch Self.orientation {
switch orientation {
case .vertical: return \.width
case .horizontal: return \.height
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/TokamakCore/Fiber/Scene/SceneVisitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ protocol SceneReducer: ViewReducer {

extension SceneReducer {
static func reduce<S: Scene>(into partialResult: inout Result, nextScene: S) {
partialResult = Self.reduce(partialResult: partialResult, nextScene: nextScene)
partialResult = reduce(partialResult: partialResult, nextScene: nextScene)
}

static func reduce<S: Scene>(partialResult: Result, nextScene: S) -> Result {
Expand Down
3 changes: 2 additions & 1 deletion Sources/TokamakCore/Fiber/ViewArguments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public struct ViewInputs<V> {

/// Mutate the underlying content with the given inputs.
///
/// Used to inject values such as environment values, traits, and preferences into the `View` type.
/// Used to inject values such as environment values, traits, and preferences into the `View`
/// type.
public let updateContent: ((inout V) -> ()) -> ()

@_spi(TokamakCore)
Expand Down
2 changes: 1 addition & 1 deletion Sources/TokamakCore/Fiber/ViewVisitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ protocol ViewReducer {

extension ViewReducer {
static func reduce<V: View>(into partialResult: inout Result, nextView: V) {
partialResult = Self.reduce(partialResult: partialResult, nextView: nextView)
partialResult = reduce(partialResult: partialResult, nextView: nextView)
}

static func reduce<V: View>(partialResult: Result, nextView: V) -> Result {
Expand Down
39 changes: 39 additions & 0 deletions Sources/TokamakCore/Gestures/Composing/ExclusiveGesture.swift
Original file line number Diff line number Diff line change
@@ -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.
//
// Created by Szymon on 16/7/2023.
//

@frozen
/// The ExclusiveGesture gives precedence to its first gesture.
public struct ExclusiveGesture<First, Second> where First: Gesture, Second: Gesture {
/// The value of an exclusive gesture that indicates which of two gestures succeeded.
public typealias Value = ExclusiveGesture.ExclusiveValue

public struct ExclusiveValue {
public var first: First.Value
public var second: First.Value
}

/// The first of two gestures.
public var first: First
/// The second of two gestures.
public var second: Second

/// Creates a gesture from two gestures where only one of them succeeds.
init(first: First, second: Second) {
self.first = first
self.second = second
}
}
39 changes: 39 additions & 0 deletions Sources/TokamakCore/Gestures/Composing/SequenceGesture.swift
Original file line number Diff line number Diff line change
@@ -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.
//
// Created by Szymon on 16/7/2023.
//

@frozen
public struct SequenceGesture<First, Second> where First: Gesture, Second: Gesture {
/// The value of a sequence gesture that helps to detect whether the first gesture succeeded, so
/// the second gesture can start.
public typealias Value = SequenceGesture.SequenceValue

public struct SequenceValue {
public var first: First.Value
public var second: First.Value
}

/// The first gesture in a sequence of two gestures.
public var first: First
/// The second gesture in a sequence of two gestures.
public var second: Second

/// Creates a sequence gesture with two gestures.
init(first: First, second: Second) {
self.first = first
self.second = second
}
}
38 changes: 38 additions & 0 deletions Sources/TokamakCore/Gestures/Composing/SimultaneousGesture.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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 Szymon on 16/7/2023.
//

@frozen
public struct SimultaneousGesture<First, Second> where First: Gesture, Second: Gesture {
public typealias Value = SimultaneousGesture.SimultaneousValue

public struct SimultaneousValue {
public let first: First.Value?
public let second: First.Value?
}

/// The first of two gestures that can happen simultaneously.
public let first: First
/// The second of two gestures that can happen simultaneously.
public let second: Second

/// Creates a gesture with two gestures that can receive updates or succeed independently of each
/// other.
init(first: First, second: Second) {
self.first = first
self.second = second
}
}
Loading
Loading