diff --git a/Examples/HelloWorld/HelloWorld.swift b/Examples/HelloWorld/HelloWorld.swift index 36748b4b..23aa2f06 100644 --- a/Examples/HelloWorld/HelloWorld.swift +++ b/Examples/HelloWorld/HelloWorld.swift @@ -28,9 +28,15 @@ extension HelloWorld: SceneDelegate { // Add a hello world label let label = Label(frame: Rect(x: 50.0, y: 100.0, width: 300.0, height: 30.0)) label.text = "Hello World" + label.addTarget(self, action: HelloWorld.onLabelPress(_:_:), + for: .primaryActionTriggered) self.window.addSubview(label) // Show the window self.window.makeKeyAndVisible() } + + private func onLabelPress(_ sender: Label, _: Control.Event) { + print("hello world"); + } } diff --git a/Sources/SwiftWin32/CMakeLists.txt b/Sources/SwiftWin32/CMakeLists.txt index 8a1da808..4ca61ede 100644 --- a/Sources/SwiftWin32/CMakeLists.txt +++ b/Sources/SwiftWin32/CMakeLists.txt @@ -106,6 +106,7 @@ target_sources(SwiftWin32 PRIVATE "Views and Controls/EdgeInsets.swift" "Views and Controls/ImageView.swift" "Views and Controls/Label.swift" + "Views and Controls/Label+Temporary.swift" "Views and Controls/ProgressView.swift" "Views and Controls/Slider.swift" "Views and Controls/Stepper.swift" diff --git a/Sources/SwiftWin32/Views and Controls/Label+Temporary.swift b/Sources/SwiftWin32/Views and Controls/Label+Temporary.swift new file mode 100644 index 00000000..39413d4a --- /dev/null +++ b/Sources/SwiftWin32/Views and Controls/Label+Temporary.swift @@ -0,0 +1,17 @@ +// Copyright (c) 2021 Yassine BENABBAS +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +extension Label { + /// Constants describing the types of events possible for controls. + public struct Event: Equatable, Hashable, RawRepresentable { + public typealias RawValue = Int + + public let rawValue: RawValue + + public init(rawValue: RawValue) { + self.rawValue = rawValue + } + } +} \ No newline at end of file diff --git a/Sources/SwiftWin32/Views and Controls/Label.swift b/Sources/SwiftWin32/Views and Controls/Label.swift index b470bea4..f349e460 100644 --- a/Sources/SwiftWin32/Views and Controls/Label.swift +++ b/Sources/SwiftWin32/Views and Controls/Label.swift @@ -20,7 +20,7 @@ private let SwiftLabelProc: SUBCLASSPROC = { (hWnd, uMsg, wParam, lParam, uIdSub return DefSubclassProc(hWnd, uMsg, wParam, lParam) } -public class Label: Control { +public class Label: View { private static let `class`: WindowClass = WindowClass(hInst: GetModuleHandleW(nil), name: "Swift.Label") private static let style: WindowStyle = (base: WS_TABSTOP, extended: 0) @@ -85,7 +85,106 @@ public class Label: Control { self.font = FontMetrics.default.scaledFont(for: self.font, compatibleWith: traitCollection) } + + + // MARK - Control API that should be removed when GestureRecognizer is implemented + private var actions: [Control.Event:[ControlEventCallable]] = + Dictionary(minimumCapacity: 16) + + @inline(__always) + private func addAction(_ callable: ControlEventCallable, + for controlEvents: Control.Event) { + for event in Label.Event.semanticEvents { + if controlEvents.rawValue & event.rawValue == event.rawValue { + self.actions[event, default: []].append(callable) + } + } + } + + func sendActions(for controlEvents: Control.Event) { + for event in Label.Event.semanticEvents { + if controlEvents.rawValue & event.rawValue == event.rawValue { + _ = self.actions[event]?.map { $0(sender: self, event: controlEvents) } + } + } + } + + public func addTarget(_ target: Target, + action: @escaping (Target) -> () -> Void, + for controlEvents: Control.Event) { + self.addAction(ControlEventCallback(binding: action, on: target), + for: controlEvents) + } + + /// Associates a target object and action method with the control. + public func addTarget(_ target: Target, + action: @escaping (Target) -> (_: Source) -> Void, + for controlEvents: Control.Event) { + self.addAction(ControlEventCallback(binding: action, on: target), + for: controlEvents) + } + + /// Returns the events for which the control has associated actions. + public private(set) var allControlEvents: Control.Event = + Control.Event(rawValue: 0) + + /// Associates a target object and action method with the control. + public func addTarget(_ target: Target, + action: @escaping (Target) -> (_: Source, _: Control.Event) -> Void, + for controlEvents: Control.Event) { + self.addAction(ControlEventCallback(binding: action, on: target), + for: controlEvents) + } } extension Label: ContentSizeCategoryAdjusting { } + + +private protocol ControlEventCallable { + func callAsFunction(sender: Label, event: Control.Event) +} + +private struct ControlEventCallback: ControlEventCallable { + private unowned(safe) let instance: Target + private let method: (Target) -> (_: Source, _: Control.Event) -> Void + + public init(binding: @escaping (Target) -> (_: Source, _: Control.Event) -> Void, + on: Target) { + self.instance = on + self.method = binding + } + + public init(binding: @escaping (Target) -> (_: Source) -> Void, on: Target) { + self.instance = on + self.method = { (target: Target) in { (sender: Source, _: Control.Event) in + binding(target)(sender) + } + } + } + + public init(binding: @escaping (Target) -> () -> Void, on: Target) { + self.instance = on + self.method = { (target: Target) in { (_: Source, _: Control.Event) in + binding(target)() + } + } + } + + public func callAsFunction(sender: Label, event: Control.Event) { + self.method(instance)(sender as! Source, event) + } +} + +extension Label.Event { + /// A semantic action triggered by buttons. + public static var primaryActionTriggered: Control.Event { + Control.Event(rawValue: 1 << 13) + } + + fileprivate static var semanticEvents: [Control.Event] { + return [ + .primaryActionTriggered + ] + } +}