-
Couldn't load subscription status.
- Fork 74
WIP: Label inherits View and clean event handling code #450
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
base: main
Are you sure you want to change the base?
Changes from 3 commits
8c765ec
ba61f8c
87efab7
6bc73ec
bdfc68b
c32ae40
f9d3ac0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,13 +46,14 @@ 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), | ||
| 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) } | ||
|
|
@@ -85,7 +73,130 @@ 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<Control.Event, [ControlEventCallable]>(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: AnyObject>(_ target: Target, | ||
| action: @escaping (Target) -> () -> Void, | ||
| for controlEvents: Control.Event) { | ||
| self.addAction(ControlEventCallback<Self, Target>(binding: action, on: target), | ||
| for: controlEvents) | ||
| } | ||
|
|
||
| /// Associates a target object and action method with the control. | ||
| public func addTarget<Source: Label, Target: AnyObject>(_ 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<Source: Label, Target: AnyObject>(_ target: Target, | ||
| action: @escaping (Target) -> (_: Source, _: Control.Event) -> Void, | ||
| for controlEvents: Control.Event) { | ||
| self.addAction(ControlEventCallback<Source, Target>(binding: action, on: target), | ||
| for: controlEvents) | ||
| } | ||
| } | ||
|
|
||
| extension Label: ContentSizeCategoryAdjusting { | ||
| } | ||
|
|
||
|
|
||
| private protocol ControlEventCallable { | ||
| func callAsFunction(sender: Label, event: Control.Event) | ||
| } | ||
|
|
||
| private struct ControlEventCallback<Source: Label, Target: AnyObject>: 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 | ||
| ] | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was there a reason for this move?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oups, I put it back in the wrong location 😁