Skip to content
Open
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
4 changes: 4 additions & 0 deletions PlayTools.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
954389CA2B392D7800B063BB /* DraggableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954389C92B392D7800B063BB /* DraggableButton.swift */; };
954389CC2B39F03D00B063BB /* EditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954389CB2B39F03D00B063BB /* EditorView.swift */; };
954389CE2B39F26600B063BB /* ElementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954389CD2B39F26600B063BB /* ElementView.swift */; };
954389D42B3A6CD000B063BB /* KeymapHUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954389D52B3A6CD000B063BB /* KeymapHUDView.swift */; };
954389D22B3A5AE100B063BB /* ElementController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954389D12B3A5AE100B063BB /* ElementController.swift */; };
9555ED2F2C058E08006E469C /* DebugView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9555ED2E2C058E08006E469C /* DebugView.swift */; };
9555ED312C058E28006E469C /* DebugModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9555ED302C058E28006E469C /* DebugModel.swift */; };
Expand Down Expand Up @@ -118,6 +119,7 @@
954389C72B392D5300B063BB /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
954389C92B392D7800B063BB /* DraggableButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableButton.swift; sourceTree = "<group>"; };
954389CB2B39F03D00B063BB /* EditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorView.swift; sourceTree = "<group>"; };
954389D52B3A6CD000B063BB /* KeymapHUDView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeymapHUDView.swift; sourceTree = "<group>"; };
954389CD2B39F26600B063BB /* ElementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementView.swift; sourceTree = "<group>"; };
954389D12B3A5AE100B063BB /* ElementController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementController.swift; sourceTree = "<group>"; };
9555ED2E2C058E08006E469C /* DebugView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -237,6 +239,7 @@
AA71978C287A481500623C15 /* CircleMenu */,
AA71979D287A481500623C15 /* EditorCircleMenu.swift */,
954389CB2B39F03D00B063BB /* EditorView.swift */,
954389D52B3A6CD000B063BB /* KeymapHUDView.swift */,
);
path = Views;
sourceTree = "<group>";
Expand Down Expand Up @@ -727,6 +730,7 @@
954389C22B38922400B063BB /* MouseArea.swift in Sources */,
AA7197AB287A481500623C15 /* Element.swift in Sources */,
AA7197AE287A481500623C15 /* EditorCircleMenu.swift in Sources */,
954389D42B3A6CD000B063BB /* KeymapHUDView.swift in Sources */,
954389D22B3A5AE100B063BB /* ElementController.swift in Sources */,
95D474FF2C0BDAB20072797F /* DraggableButtonElement.swift in Sources */,
9562D1622AB4FE73002C329D /* CameraControlMouseEventAdapter.swift in Sources */,
Expand Down
131 changes: 102 additions & 29 deletions PlayTools/Controls/ActionDispatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ public enum ActionDispatchPriority: Int {
public class ActionDispatcher {
static private let keymapVersion = "2.0."
static private var actions = [Action]()
static private var buttonHandlers: [String: [(Bool) -> Void]] = [:]
static private var buttonHandlers: [String: [ButtonPressHandler]] = [:]
static private var pressedKeys = Set<String>()

static private let priorityCount = 3
// You can't put more than 8 cameras or 8 joysticks in a keymap right?
static private let mappingCountPerPriority = 8
static private let mappingCountPerPriority = 16
static private let directionPadHandlers: [[ManagedAtomic<AtomicHandler>]] = Array(
(0..<priorityCount).map({_ in
(0..<mappingCountPerPriority).map({_ in ManagedAtomic<AtomicHandler>(.EMPTY)})
Expand All @@ -35,6 +36,7 @@ public class ActionDispatcher {
invalidateActions()
actions = []
buttonHandlers.removeAll(keepingCapacity: true)
pressedKeys.removeAll(keepingCapacity: true)
directionPadHandlers.forEach({ handlers in
handlers.forEach({ handler in
handler.store(.EMPTY, ordering: .relaxed)
Expand All @@ -49,6 +51,9 @@ public class ActionDispatcher {
clear()

actions.append(FakeMouseAction())
if ShoulderKeymapSwitchAction.isEnabled {
actions.append(ShoulderKeymapSwitchAction())
}

// current keymap version is 2.0.x.
// in future, keymap format will be upgraded.
Expand Down Expand Up @@ -79,6 +84,14 @@ public class ActionDispatcher {
actions.append(CameraAction(data: mouse))
}

for swipe in keymap.currentKeymap.swipeModels {
actions.append(TriggeredSwipeAction(data: swipe))
}

for radialSelector in keymap.currentKeymap.radialSelectorModels {
actions.append(RadialSelectorAction(data: radialSelector))
}

for joystick in keymap.currentKeymap.joystickModel {
// Left Thumbstick, Right Thumbstick, Mouse
if JoystickModel.isAnalog(joystick) {
Expand All @@ -96,18 +109,32 @@ public class ActionDispatcher {
}

static public func register(key: String, handler: @escaping (Bool) -> Void) {
register(key: key, modifierKeys: [], handler: handler)
}

static public func register(key: String,
modifierKeys: [String],
handler: @escaping (Bool) -> Void) {
// this function is called when setting up `button` type of mapping
if buttonHandlers[key] == nil {
buttonHandlers[key] = []
}
buttonHandlers[key]!.append(handler)
buttonHandlers[key]!.append(ButtonPressHandler(modifierKeys: modifierKeys, handle: handler))
}

static public func register(key: String,
handler: @escaping (CGFloat, CGFloat) -> Void,
priority: ActionDispatchPriority = .DEFAULT) {
let atomicHandler = directionPadHandlers[priority.rawValue].first(where: { handler in
handler.load(ordering: .relaxed).key == key
register(key: key, modifierKeys: [], handler: handler, priority: priority)
}

static public func register(key: String,
modifierKeys: [String],
handler: @escaping (CGFloat, CGFloat) -> Void,
priority: ActionDispatchPriority = .DEFAULT) {
let atomicHandler = directionPadHandlers[priority.rawValue].first(where: { storedHandler in
let handlerValue = storedHandler.load(ordering: .relaxed)
return handlerValue.key == key && handlerValue.modifierKeys == modifierKeys
}) ??
directionPadHandlers[priority.rawValue].first(where: { handler in
handler.load(ordering: .relaxed).key.isEmpty
Expand All @@ -119,13 +146,16 @@ public class ActionDispatcher {
// Toast.showHint(title: "register",
// text: ["key: \(key), atomicHandler: \(String(describing: atomicHandler))"])
// }
atomicHandler?.store(AtomicHandler(key, handler), ordering: .releasing)
atomicHandler?.store(AtomicHandler(key, modifierKeys, handler), ordering: .releasing)
}

static public func unregister(key: String) {
static public func unregister(key: String,
modifierKeys: [String] = [],
priority: ActionDispatchPriority = .DRAGGABLE) {
// Only draggable can be unregistered
let atomicHandler = directionPadHandlers[ActionDispatchPriority.DRAGGABLE.rawValue].first(where: { handler in
handler.load(ordering: .relaxed).key == key
let atomicHandler = directionPadHandlers[priority.rawValue].first(where: { handler in
let handlerValue = handler.load(ordering: .relaxed)
return handlerValue.key == key && handlerValue.modifierKeys == modifierKeys
})
// DispatchQueue.main.async {
// if screen.keyWindow == nil {
Expand Down Expand Up @@ -172,57 +202,100 @@ public class ActionDispatcher {
}

static public func getDispatchPriority(key: String) -> ActionDispatchPriority? {
if let priority = directionPadHandlers.firstIndex(where: { handlers in
handlers.contains(where: { handler in
handler.load(ordering: .acquiring).key == key
})
}) {
// Toast.showHint(title: "\(key) priority", text: ["\(priority)"])
return ActionDispatchPriority(rawValue: priority)
for priority in 0..<priorityCount {
let handlers = directionPadHandlers[priority]
.map { $0.load(ordering: .acquiring) }
.filter { $0.key == key }
guard !handlers.isEmpty else {
continue
}

let modifiedHandlers = handlers.filter { handler in
!handler.modifierKeys.isEmpty && isPressed(anyOf: handler.modifierKeys)
}
if !modifiedHandlers.isEmpty || handlers.contains(where: { $0.modifierKeys.isEmpty }) {
return ActionDispatchPriority(rawValue: priority)
}
}

if buttonHandlers[key] != nil {
if let handlers = buttonHandlers[key],
handlers.contains(where: { handler in
handler.modifierKeys.isEmpty || isPressed(anyOf: handler.modifierKeys)
}) {
return .DEFAULT
}
return nil
}

static public func dispatch(key: String, pressed: Bool) -> Bool {
if pressed {
pressedKeys.insert(key)
} else {
pressedKeys.remove(key)
}
guard let handlers = buttonHandlers[key] else {
return false
}
var mapped = false
for handler in handlers {
let modifiedHandlers = handlers.filter { handler in
!handler.modifierKeys.isEmpty && isPressed(anyOf: handler.modifierKeys)
}
let selectedHandlers = modifiedHandlers.isEmpty ?
handlers.filter { $0.modifierKeys.isEmpty } :
modifiedHandlers
for handler in selectedHandlers {
PlayInput.touchQueue.async(qos: .userInteractive, execute: {
handler(pressed)
handler.handle(pressed)
})
mapped = true
}
// return value matters. A false value makes a beep sound
return mapped
return !selectedHandlers.isEmpty
}

static public func isPressed(anyOf keys: [String]) -> Bool {
keys.contains { pressedKeys.contains($0) }
}

static public func dispatch(key: String, valueX: CGFloat, valueY: CGFloat) -> Bool {
for priority in 0..<priorityCount {
if let handler = directionPadHandlers[priority].first(where: { handler in
handler.load(ordering: .acquiring).key == key
}) {
let handlers = directionPadHandlers[priority]
.map { $0.load(ordering: .acquiring) }
.filter { $0.key == key }
guard !handlers.isEmpty else {
continue
}
let modifiedHandlers = handlers.filter { handler in
!handler.modifierKeys.isEmpty && isPressed(anyOf: handler.modifierKeys)
}
let selectedHandlers = modifiedHandlers.isEmpty
? handlers.filter { $0.modifierKeys.isEmpty }
: modifiedHandlers
guard !selectedHandlers.isEmpty else {
continue
}
for handler in selectedHandlers {
PlayInput.touchQueue.async(qos: .userInteractive, execute: {
handler.load(ordering: .relaxed).handle(valueX, valueY)
handler.handle(valueX, valueY)
})
return true
}
return true
}
return false
}
}

private struct ButtonPressHandler {
let modifierKeys: [String]
let handle: (Bool) -> Void
}

private final class AtomicHandler: AtomicReference {
static fileprivate let EMPTY = AtomicHandler("", {_, _ in })
static fileprivate let EMPTY = AtomicHandler("", [], {_, _ in })
let key: String
let modifierKeys: [String]
let handle: (CGFloat, CGFloat) -> Void
init(_ key: String, _ handle: @escaping (CGFloat, CGFloat) -> Void) {
init(_ key: String, _ modifierKeys: [String], _ handle: @escaping (CGFloat, CGFloat) -> Void) {
self.key = key
self.modifierKeys = modifierKeys
self.handle = handle
}
}
Loading