Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
195 changes: 2 additions & 193 deletions Sources/OpenSwiftUICore/Event/Event/Event.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,161 +2,10 @@
// Event.swift
// OpenSwiftUICore
//
// Audited for 6.5.4
// Status: Complete

package import Foundation

// MARK: - MouseEvent [6.5.4]

package struct MouseEvent: SpatialEventType, TappableEventType, ModifiersEventType, Equatable {
package struct Button: RawRepresentable, Equatable {
package let rawValue: Int

package init(rawValue: Int) {
self.rawValue = rawValue
}

package static let primary: MouseEvent.Button = .init(rawValue: 1 << 0)

package static let secondary: MouseEvent.Button = .init(rawValue: 1 << 1)

package static func other(_ index: Int) -> MouseEvent.Button {
.init(rawValue: index)
}
}

package var timestamp: Time
package var binding: EventBinding?
package var button: MouseEvent.Button
package var phase: EventPhase
package var location: CGPoint
package var globalLocation: CGPoint
package var modifiers: EventModifiers

package init(
timestamp: Time,
binding: EventBinding? = nil,
button: MouseEvent.Button,
phase: EventPhase,
location: CGPoint,
globalLocation: CGPoint,
modifiers: EventModifiers
) {
self.timestamp = timestamp
self.binding = binding
self.button = button
self.phase = phase
self.location = location
self.globalLocation = globalLocation
self.modifiers = modifiers
}

package var radius: CGFloat { .zero }

package var kind: SpatialEvent.Kind? { .mouse }
}

extension MouseEvent: HitTestableEventType {}

// MARK: - EventModifiers [6.5.4]

/// A set of key modifiers that you can add to a gesture.
@available(OpenSwiftUI_v1_0, *)
@frozen
public struct EventModifiers: OptionSet {
public let rawValue: Int

public init(rawValue: Int) {
self.rawValue = rawValue
}

/// The Caps Lock key.
public static let capsLock: EventModifiers = .init(rawValue: 1 << 0)

/// The Shift key.
public static let shift: EventModifiers = .init(rawValue: 1 << 1)

/// The Control key.
public static let control: EventModifiers = .init(rawValue: 1 << 2)

/// The Option key.
public static let option: EventModifiers = .init(rawValue: 1 << 3)

/// The Command key.
public static let command: EventModifiers = .init(rawValue: 1 << 4)

/// Any key on the numeric keypad.
public static let numericPad: EventModifiers = .init(rawValue: 1 << 5)

/// The Function key.
@available(*, deprecated, message: "Function modifier is reserved for system applications")
public static let function: EventModifiers = .init(rawValue: 1 << 6)

package static let _function: EventModifiers = .init(rawValue: 1 << 6)

/// All possible modifier keys.
public static let all: EventModifiers = [.capsLock, .shift, .control, .option, .command, .numericPad]

package static let _all: EventModifiers = [.capsLock, .shift, .control, .option, .command, .numericPad, ._function]
}

// MARK: - ModifiersEventType [6.5.4]

package protocol ModifiersEventType: EventType {
var modifiers: EventModifiers { get set }
}

// MARK: - EventPhase [6.5.4]

@_spi(ForOpenSwiftUIOnly)
@available(OpenSwiftUI_v6_0, *)
public enum EventPhase: Hashable {
case began
case active
case ended
case failed
}

@available(*, unavailable)
extension EventPhase: Sendable {}

extension EventPhase {
package var isTerminal: Bool {
switch self {
case .began, .active: false
case .ended, .failed: true
}
}
}

// MARK: - EventType [6.5.4]

@_spi(ForOpenSwiftUIOnly)
@available(OpenSwiftUI_v6_0, *)
public protocol EventType {
var phase: EventPhase { get }

var timestamp: Time { get }

var binding: EventBinding? { get set }

init?(_ event: any EventType)
}

extension EventType {
package init?(_ event: any EventType) {
guard let event = event as? Self else {
return nil
}
self = event
}

package var isFocusEvent: Bool {
HitTestableEvent(self) == nil
}
}

// MARK: - Event [6.5.4]
// MARK: - Event

package struct Event: EventType {
package var phase: EventPhase
Expand All @@ -175,43 +24,3 @@ package struct Event: EventType {
binding = event.binding
}
}

// MARK: - EventID [6.5.4]

@_spi(ForOpenSwiftUIOnly)
@available(OpenSwiftUI_v6_0, *)
public struct EventID: Hashable {
package var type: any Any.Type

package var serial: Int

package init(type: any Any.Type, serial: Int) {
self.type = type
self.serial = serial
}

public static func == (lhs: EventID, rhs: EventID) -> Bool {
lhs.type == rhs.type && lhs.serial == rhs.serial
}

public func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(type))
hasher.combine(serial)
}
}

@available(*, unavailable)
extension EventID: Sendable {}

extension EventID {
package init<T, S>(_ obj: T, subtype: S.Type) where T: NSObject {
type = (T, S).self
serial = unsafeBitCast(obj, to: Int.self)
}
}

extension EventID: CustomStringConvertible {
public var description: String {
"\(type)#\(serial)"
}
}
3 changes: 2 additions & 1 deletion Sources/OpenSwiftUICore/Event/Event/EventBinding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
// EventBinding.swift
// OpenSwiftUICore
//
// Audited for 6.5.4
// Status: Complete

// MARK: - EventBinding [6.5.4]
// MARK: - EventBinding

@_spi(ForOpenSwiftUIOnly)
@available(OpenSwiftUI_v6_0, *)
Expand Down
93 changes: 83 additions & 10 deletions Sources/OpenSwiftUICore/Event/Event/EventBindingBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
// EventBindingBridge.swift
// OpenSwiftUICore
//
// Status: WIP
// Audited for 6.5.4
// Status: Complete
// ID: E11AC34B5BFF53E1001A61D61F5B9E0F (SwiftUICore)

// MARK: - EventBindingBridge [6.5.4] [WIP]
// MARK: - EventBindingBridge

@_spi(ForOpenSwiftUIOnly)
@available(OpenSwiftUI_v6_0, *)
Expand All @@ -25,27 +26,87 @@ open class EventBindingBridge {
self.eventBindingManager = eventBindingManager
}

public init() {}
public init() {
_openSwiftUIEmptyStub()
}

open var eventSources: [any EventBindingSource] { [] }

// MARK: - Event dispatch tracking

@discardableResult
open func send(
_ events: [EventID: any EventType],
source: any EventBindingSource
) -> Set<EventID> {
_openSwiftUIUnimplementedFailure()
guard !events.isEmpty else {
return []
}
var downstreamEvents: [EventID: any EventType] = [:]
let sourceID = ObjectIdentifier(source)
for (id, event) in events {
guard !(event is any NonGestureEventType) else {
_ = Event(event)
downstreamEvents[id] = event
continue
}
let trackedEvent: TrackedEventState
let reset: Bool
if let existingState = trackedEvents[id] {
trackedEvent = existingState
reset = trackedEvent.reset
} else {
trackedEvent = TrackedEventState(
sourceID: sourceID,
reset: false
)
reset = false
}
if event.phase == .active {
trackedEvents[id] = trackedEvent
} else {
trackedEvents.removeValue(forKey: id)
}
if !reset {
downstreamEvents[id] = event
}
}
guard !downstreamEvents.isEmpty,
let eventBindingManager else {
return []
}
return eventBindingManager.send(downstreamEvents)
}

// MARK: - Event source reset

open func reset(
eventSource: any EventBindingSource,
resetForwardedEventDispatchers: Bool = false
) {
_openSwiftUIUnimplementedFailure()
let sourceID = ObjectIdentifier(eventSource)
var hasActiveEvent = false
for (id, trackedEvent) in trackedEvents {
if trackedEvent.sourceID == sourceID {
trackedEvents.removeValue(forKey: id)
} else if !trackedEvent.reset {
hasActiveEvent = true
}
}
guard !hasActiveEvent else {
return
}
eventBindingManager?.reset(
resetForwardedEventDispatchers: resetForwardedEventDispatchers
)
}

private func resetEvent() {
_openSwiftUIUnimplementedFailure()
private func resetEvents() {
for (id, trackedEvent) in trackedEvents {
var trackedEvent = trackedEvent
trackedEvent.reset = true
trackedEvents[id] = trackedEvent
}
}

open func setInheritedPhase(_ phase: _GestureInputs.InheritedPhase) {
Expand All @@ -61,7 +122,7 @@ open class EventBindingBridge {
@available(*, unavailable)
extension EventBindingBridge: Sendable {}

// MARK: - EventBindingBridge + EventBindingManagerDelegate [6.5.4]
// MARK: - EventBindingBridge + EventBindingManagerDelegate

@_spi(ForOpenSwiftUIOnly)
extension EventBindingBridge: EventBindingManagerDelegate {
Expand All @@ -70,8 +131,7 @@ extension EventBindingBridge: EventBindingManagerDelegate {
id: EventID
) {
if let responderWasBoundHandler {
// TODO: Update.enqueueAction(reason:_:)
Update.enqueueAction {
Update.enqueueAction(reason: nil) {
responderWasBoundHandler(newBinding.responder)
}
}
Expand All @@ -87,6 +147,9 @@ extension EventBindingBridge: EventBindingManagerDelegate {
for eventSource in eventSources {
eventSource.didUpdate(phase: phase, in: self)
}
if phase.isTerminal {
resetEvents()
}
}

package func didUpdate(
Expand All @@ -97,4 +160,14 @@ extension EventBindingBridge: EventBindingManagerDelegate {
eventSource.didUpdate(gestureCategory: gestureCategory, in: self)
}
}

#if os(macOS)
package func requestHoverUpdate(
in eventBindingManager: EventBindingManager
) {
for eventSource in eventSources {
eventSource.didRequestHoverUpdate(in: self)
}
}
#endif
}
Loading
Loading