From 8c765ec1b9ec4d0a76bc5ff23373c2b7c004a8ef Mon Sep 17 00:00:00 2001 From: yostane Date: Thu, 8 Apr 2021 15:55:19 +0200 Subject: [PATCH 1/7] Label inherits View and clean event handling code --- .../SwiftWin32/Views and Controls/Label.swift | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/Sources/SwiftWin32/Views and Controls/Label.swift b/Sources/SwiftWin32/Views and Controls/Label.swift index b470bea4..14d602bc 100644 --- a/Sources/SwiftWin32/Views and Controls/Label.swift +++ b/Sources/SwiftWin32/Views and Controls/Label.swift @@ -7,20 +7,7 @@ import WinSDK -private let SwiftLabelProc: SUBCLASSPROC = { (hWnd, uMsg, wParam, lParam, uIdSubclass, dwRefData) in - let label: Label? = unsafeBitCast(dwRefData, to: AnyObject.self) as? Label - - switch uMsg { - case UINT(WM_LBUTTONUP): - label?.sendActions(for: .primaryActionTriggered) - default: - break - } - - 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) @@ -59,8 +46,6 @@ public class Label: Control { public init(frame: Rect) { super.init(frame: frame, class: Label.class, style: Label.style) - _ = SetWindowSubclass(hWnd, SwiftLabelProc, UINT_PTR(1), - unsafeBitCast(self as AnyObject, to: DWORD_PTR.self)) let size = self.frame.size self.hWnd_ = CreateWindowExW(0, WC_STATIC.wide, nil, DWORD(WS_CHILD), From ba61f8cb9d6b025954d6298f5901e1967ece19c5 Mon Sep 17 00:00:00 2001 From: yostane Date: Wed, 14 Apr 2021 15:36:27 +0200 Subject: [PATCH 2/7] Attempt to backport event handling code into Label --- Examples/HelloWorld/HelloWorld.swift | 6 + .../SwiftWin32/Views and Controls/Label.swift | 127 ++++++++++++++++++ 2 files changed, 133 insertions(+) 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/Views and Controls/Label.swift b/Sources/SwiftWin32/Views and Controls/Label.swift index 14d602bc..4921f446 100644 --- a/Sources/SwiftWin32/Views and Controls/Label.swift +++ b/Sources/SwiftWin32/Views and Controls/Label.swift @@ -51,6 +51,9 @@ public class Label: View { self.hWnd_ = CreateWindowExW(0, WC_STATIC.wide, nil, DWORD(WS_CHILD), 0, 0, CInt(size.width), CInt(size.height), self.hWnd, nil, GetModuleHandleW(nil), nil)! + + _ = SetWindowSubclass(hWnd, SwiftLabelProc, UINT_PTR(1), + unsafeBitCast(self as AnyObject, to: DWORD_PTR.self)) // Perform the font setting in `defer` which ensures that the property // observer is triggered. defer { self.font = Font.systemFont(ofSize: Font.systemFontSize) } @@ -70,7 +73,131 @@ public class Label: View { 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) { + print("adding action") + 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) + } +} + +private let SwiftLabelProc: SUBCLASSPROC = { (hWnd, uMsg, wParam, lParam, uIdSubclass, dwRefData) in + let label: Label? = unsafeBitCast(dwRefData, to: AnyObject.self) as? Label + + switch uMsg { + case UINT(WM_LBUTTONUP): + label?.sendActions(for: .primaryActionTriggered) + default: + break + } + + return DefSubclassProc(hWnd, uMsg, wParam, lParam) +} +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 + } + } +} +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 + ] + } +} From 87efab70166871fd4e0013d4036c0acd4b28cc50 Mon Sep 17 00:00:00 2001 From: yostane Date: Wed, 14 Apr 2021 15:37:29 +0200 Subject: [PATCH 3/7] Remove some test code --- Sources/SwiftWin32/Views and Controls/Label.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/SwiftWin32/Views and Controls/Label.swift b/Sources/SwiftWin32/Views and Controls/Label.swift index 4921f446..14f2e8b4 100644 --- a/Sources/SwiftWin32/Views and Controls/Label.swift +++ b/Sources/SwiftWin32/Views and Controls/Label.swift @@ -120,7 +120,6 @@ public class Label: View { public func addTarget(_ target: Target, action: @escaping (Target) -> (_: Source, _: Control.Event) -> Void, for controlEvents: Control.Event) { - print("adding action") self.addAction(ControlEventCallback(binding: action, on: target), for: controlEvents) } From 6bc73ec00cc9de43d5e3418e4c5f4efc94a69a99 Mon Sep 17 00:00:00 2001 From: yostane Date: Thu, 22 Apr 2021 10:07:00 +0200 Subject: [PATCH 4/7] Reverted code to original position --- .../SwiftWin32/Views and Controls/Label.swift | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Sources/SwiftWin32/Views and Controls/Label.swift b/Sources/SwiftWin32/Views and Controls/Label.swift index 14f2e8b4..5a062525 100644 --- a/Sources/SwiftWin32/Views and Controls/Label.swift +++ b/Sources/SwiftWin32/Views and Controls/Label.swift @@ -7,6 +7,19 @@ import WinSDK +private let SwiftLabelProc: SUBCLASSPROC = { (hWnd, uMsg, wParam, lParam, uIdSubclass, dwRefData) in + let label: Label? = unsafeBitCast(dwRefData, to: AnyObject.self) as? Label + + switch uMsg { + case UINT(WM_LBUTTONUP): + label?.sendActions(for: .primaryActionTriggered) + default: + break + } + + return DefSubclassProc(hWnd, uMsg, wParam, lParam) +} + public class Label: View { private static let `class`: WindowClass = WindowClass(hInst: GetModuleHandleW(nil), name: "Swift.Label") @@ -46,14 +59,13 @@ public class Label: View { public init(frame: Rect) { super.init(frame: frame, class: Label.class, style: Label.style) - + _ = SetWindowSubclass(hWnd, SwiftLabelProc, UINT_PTR(1), + unsafeBitCast(self as AnyObject, to: DWORD_PTR.self)) + let size = self.frame.size self.hWnd_ = CreateWindowExW(0, WC_STATIC.wide, nil, DWORD(WS_CHILD), 0, 0, CInt(size.width), CInt(size.height), self.hWnd, nil, GetModuleHandleW(nil), nil)! - - _ = SetWindowSubclass(hWnd, SwiftLabelProc, UINT_PTR(1), - unsafeBitCast(self as AnyObject, to: DWORD_PTR.self)) // Perform the font setting in `defer` which ensures that the property // observer is triggered. defer { self.font = Font.systemFont(ofSize: Font.systemFontSize) } @@ -164,18 +176,6 @@ private struct ControlEventCallback: ControlEv } } -private let SwiftLabelProc: SUBCLASSPROC = { (hWnd, uMsg, wParam, lParam, uIdSubclass, dwRefData) in - let label: Label? = unsafeBitCast(dwRefData, to: AnyObject.self) as? Label - - switch uMsg { - case UINT(WM_LBUTTONUP): - label?.sendActions(for: .primaryActionTriggered) - default: - break - } - - return DefSubclassProc(hWnd, uMsg, wParam, lParam) -} extension Label { /// Constants describing the types of events possible for controls. public struct Event: Equatable, Hashable, RawRepresentable { From bdfc68b733ed9f90e80f8ed7dcb3f3ed4222a289 Mon Sep 17 00:00:00 2001 From: yostane Date: Thu, 22 Apr 2021 10:10:53 +0200 Subject: [PATCH 5/7] Remove extra space --- Sources/SwiftWin32/Views and Controls/Label.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftWin32/Views and Controls/Label.swift b/Sources/SwiftWin32/Views and Controls/Label.swift index 5a062525..9569399f 100644 --- a/Sources/SwiftWin32/Views and Controls/Label.swift +++ b/Sources/SwiftWin32/Views and Controls/Label.swift @@ -60,8 +60,8 @@ public class Label: View { public init(frame: Rect) { super.init(frame: frame, class: Label.class, style: Label.style) _ = SetWindowSubclass(hWnd, SwiftLabelProc, UINT_PTR(1), - unsafeBitCast(self as AnyObject, to: DWORD_PTR.self)) - + unsafeBitCast(self as AnyObject, to: DWORD_PTR.self)) + let size = self.frame.size self.hWnd_ = CreateWindowExW(0, WC_STATIC.wide, nil, DWORD(WS_CHILD), 0, 0, CInt(size.width), CInt(size.height), From c32ae40aa52dd58ca655fb877c31d77200a689ac Mon Sep 17 00:00:00 2001 From: yostane Date: Thu, 22 Apr 2021 10:11:35 +0200 Subject: [PATCH 6/7] Fix extra space --- Sources/SwiftWin32/Views and Controls/Label.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwiftWin32/Views and Controls/Label.swift b/Sources/SwiftWin32/Views and Controls/Label.swift index 9569399f..bcbf936e 100644 --- a/Sources/SwiftWin32/Views and Controls/Label.swift +++ b/Sources/SwiftWin32/Views and Controls/Label.swift @@ -60,7 +60,7 @@ public class Label: View { public init(frame: Rect) { super.init(frame: frame, class: Label.class, style: Label.style) _ = SetWindowSubclass(hWnd, SwiftLabelProc, UINT_PTR(1), - unsafeBitCast(self as AnyObject, to: DWORD_PTR.self)) + unsafeBitCast(self as AnyObject, to: DWORD_PTR.self)) let size = self.frame.size self.hWnd_ = CreateWindowExW(0, WC_STATIC.wide, nil, DWORD(WS_CHILD), From f9d3ac08776acf0b4db323a4a1c48571a19e5b04 Mon Sep 17 00:00:00 2001 From: yostane Date: Tue, 27 Apr 2021 12:10:51 +0200 Subject: [PATCH 7/7] initial code in label+temporary --- Sources/SwiftWin32/CMakeLists.txt | 1 + .../Views and Controls/Label+Temporary.swift | 17 +++++++++++++++++ .../SwiftWin32/Views and Controls/Label.swift | 12 ------------ 3 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 Sources/SwiftWin32/Views and Controls/Label+Temporary.swift 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 bcbf936e..f349e460 100644 --- a/Sources/SwiftWin32/Views and Controls/Label.swift +++ b/Sources/SwiftWin32/Views and Controls/Label.swift @@ -176,18 +176,6 @@ private struct ControlEventCallback: ControlEv } } -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 - } - } -} extension Label.Event { /// A semantic action triggered by buttons. public static var primaryActionTriggered: Control.Event {