Skip to content

Commit 9bfde06

Browse files
committed
Button: add support for Action based handling
This allows us to use the newer `Action` based handler to build up interfaces. It extends `Control` to invoke an `ActionHandler` rather than work solely with the target-action pattern.
1 parent 6a05316 commit 9bfde06

File tree

4 files changed

+59
-0
lines changed

4 files changed

+59
-0
lines changed

Sources/SwiftWin32/Menus and Shortcuts/Action.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ open class Action: MenuElement {
3535
self.discoverabilityTitle = discoverabilityTitle
3636
self.attributes = attributes
3737
self.state = state
38+
self.handler = handler
3839

3940
super.init(title: title, image: image)
4041
}
@@ -70,4 +71,6 @@ open class Action: MenuElement {
7071

7172
/// The object responsible for the action handler.
7273
open internal(set) var sender: Any?
74+
75+
internal private(set) var handler: ActionHandler
7376
}

Sources/SwiftWin32/Views and Controls/Button.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ public class Button: Control {
3939
unsafeBitCast(self as AnyObject, to: DWORD_PTR.self))
4040
}
4141

42+
/// Creates a new button with the specified frame, registers the primary
43+
/// action event, and sets the title and image to the action’s title and
44+
/// image.
45+
public convenience init(frame: Rect, primaryAction: Action?) {
46+
self.init(frame: frame)
47+
if let action = primaryAction {
48+
self.setTitle(action.title, forState: .normal)
49+
self.addAction(action, for: .primaryActionTriggered)
50+
}
51+
}
52+
4253
// MARK - Configuring the Button Title
4354

4455
/// Sets the title to use for the specified state.

Sources/SwiftWin32/Views and Controls/Control.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ private struct ControlEventCallback<Source: Control, Target: AnyObject>: Control
3636
}
3737
}
3838

39+
extension ControlEventCallback where Target: Action {
40+
fileprivate init(invoking action: Target) {
41+
self.instance = action
42+
self.method = { (_: Target) in { (sender: Source, _: Control.Event) in
43+
action.sender = sender
44+
defer { action.sender = nil }
45+
action.handler(action)
46+
}
47+
}
48+
}
49+
}
50+
3951
@inline(__always)
4052
private var kTriggers: [Control.Event] {
4153
Control.Event.touchEvents + Control.Event.semanticEvents + Control.Event.editingEvents
@@ -56,6 +68,11 @@ public class Control: View {
5668
.forEach { self.actions[$0, default: []].append(callable) }
5769
}
5870

71+
internal func addAction(_ action: Action, for controlEvents: Control.Event) {
72+
kTriggers.filter { $0.rawValue & controlEvents.rawValue == $0.rawValue }
73+
.forEach { self.actions[$0, default: []].append(ControlEventCallback(invoking: action)) }
74+
}
75+
5976
/// Associates a target object and action method with the control.
6077
public func addTarget<Target: AnyObject>(_ target: Target,
6178
action: @escaping (Target) -> () -> Void,

Tests/UICoreTests/ButtonTests.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright © 2021 Saleem Abdulrasool <[email protected]>
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
4+
import XCTest
5+
import WinSDK
6+
@testable import SwiftWin32
7+
8+
final class ButtonTests: XCTestCase {
9+
func testConstructWithAction() {
10+
let expectation: XCTestExpectation =
11+
self.expectation(description: "action is performed")
12+
13+
let button: Button =
14+
Button(frame: .zero, primaryAction: Action { _ in
15+
expectation.fulfill()
16+
})
17+
18+
// TODO(compnerd) migrate to UI automation API
19+
_ = SendMessageW(button.hWnd, UINT(WM_LBUTTONUP), 0, 0)
20+
21+
// FIXME(compnerd) what is a good timeout value to use?
22+
waitForExpectations(timeout: 1)
23+
}
24+
25+
static var allTests = [
26+
("testConstructWithAction", testConstructWithAction),
27+
]
28+
}

0 commit comments

Comments
 (0)