Skip to content

Commit fed7ceb

Browse files
committed
Impl @Environment property wrapper, impl file open/save dialog support
The @Environment property wrapper work should make @State much easier. In this commit I also cleaned up both Gtk wrappers quite significantly with a new root GObject class and a focus on convenience initializers instead of designated initializers (which are both more correct and simpler to generate due to not requiring us to determine which inits are overrides and which aren't). I would've done @Environment and file dialog support separately but I only started on @Environment because it was required for file dialog support, so it all got a bit jumbled together.
1 parent 052cd68 commit fed7ceb

File tree

148 files changed

+3854
-2472
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

148 files changed

+3854
-2472
lines changed

Sources/AppKitBackend/AppKitBackend.swift

Lines changed: 80 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ public final class AppKitBackend: AppBackend {
176176
}
177177
}
178178

179-
public func computeRootEnvironment(defaultEnvironment: Environment) -> Environment {
179+
public func computeRootEnvironment(defaultEnvironment: EnvironmentValues) -> EnvironmentValues {
180180
let isDark = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark"
181181
let font = Font.system(
182182
size: Int(NSFont.systemFont(ofSize: 0.0).pointSize.rounded(.awayFromZero))
@@ -358,7 +358,7 @@ public final class AppKitBackend: AppBackend {
358358
of text: String,
359359
whenDisplayedIn textView: Widget,
360360
proposedFrame: SIMD2<Int>?,
361-
environment: Environment
361+
environment: EnvironmentValues
362362
) -> SIMD2<Int> {
363363
if let proposedFrame, proposedFrame.x == 0 {
364364
// We want the text to have the same height as it would have if it were
@@ -400,7 +400,11 @@ public final class AppKitBackend: AppBackend {
400400
return field
401401
}
402402

403-
public func updateTextView(_ textView: Widget, content: String, environment: Environment) {
403+
public func updateTextView(
404+
_ textView: Widget,
405+
content: String,
406+
environment: EnvironmentValues
407+
) {
404408
let field = textView as! NSTextField
405409
field.attributedStringValue = Self.attributedString(for: content, in: environment)
406410
}
@@ -413,7 +417,7 @@ public final class AppKitBackend: AppBackend {
413417
_ button: Widget,
414418
label: String,
415419
action: @escaping () -> Void,
416-
environment: Environment
420+
environment: EnvironmentValues
417421
) {
418422
let button = button as! NSButton
419423
button.attributedTitle = Self.attributedString(for: label, in: environment)
@@ -494,7 +498,7 @@ public final class AppKitBackend: AppBackend {
494498
public func updatePicker(
495499
_ picker: Widget,
496500
options: [String],
497-
environment: Environment,
501+
environment: EnvironmentValues,
498502
onChange: @escaping (Int?) -> Void
499503
) {
500504
let picker = picker as! NSPopUpButton
@@ -528,7 +532,7 @@ public final class AppKitBackend: AppBackend {
528532
public func updateTextField(
529533
_ textField: Widget,
530534
placeholder: String,
531-
environment: Environment,
535+
environment: EnvironmentValues,
532536
onChange: @escaping (String) -> Void
533537
) {
534538
let textField = textField as! NSObservableTextField
@@ -696,7 +700,7 @@ public final class AppKitBackend: AppBackend {
696700
public func setColumnLabels(
697701
ofTable table: Widget,
698702
to labels: [String],
699-
environment: Environment
703+
environment: EnvironmentValues
700704
) {
701705
let table = (table as! NSScrollView).documentView as! NSCustomTableView
702706
var columnIndices: [ObjectIdentifier: Int] = [:]
@@ -735,7 +739,7 @@ public final class AppKitBackend: AppBackend {
735739

736740
private static func attributedString(
737741
for text: String,
738-
in environment: Environment
742+
in environment: EnvironmentValues
739743
) -> NSAttributedString {
740744
NSAttributedString(
741745
string: text,
@@ -744,7 +748,7 @@ public final class AppKitBackend: AppBackend {
744748
}
745749

746750
private static func attributes(
747-
forTextIn environment: Environment
751+
forTextIn environment: EnvironmentValues
748752
) -> [NSAttributedString.Key: Any] {
749753
let paragraphStyle = NSMutableParagraphStyle()
750754
paragraphStyle.alignment =
@@ -763,7 +767,7 @@ public final class AppKitBackend: AppBackend {
763767
]
764768
}
765769

766-
private static func font(for environment: Environment) -> NSFont {
770+
private static func font(for environment: EnvironmentValues) -> NSFont {
767771
switch environment.font {
768772
case .system(let size, let weight, let design):
769773
switch design {
@@ -823,7 +827,7 @@ public final class AppKitBackend: AppBackend {
823827
public func updateProgressBar(
824828
_ widget: Widget,
825829
progressFraction: Double?,
826-
environment: Environment
830+
environment: EnvironmentValues
827831
) {
828832
let progressBar = widget as! NSProgressIndicator
829833
progressBar.doubleValue = progressFraction ?? 0
@@ -847,7 +851,7 @@ public final class AppKitBackend: AppBackend {
847851
public func updatePopoverMenu(
848852
_ menu: Menu,
849853
content: ResolvedMenu,
850-
environment: Environment
854+
environment: EnvironmentValues
851855
) {
852856
menu.appearance = environment.colorScheme.nsAppearance
853857
menu.items = Self.renderMenuItems(content.items)
@@ -876,7 +880,7 @@ public final class AppKitBackend: AppBackend {
876880
_ alert: Alert,
877881
title: String,
878882
actionLabels: [String],
879-
environment: Environment
883+
environment: EnvironmentValues
880884
) {
881885
alert.messageText = title
882886
for label in actionLabels {
@@ -908,6 +912,69 @@ public final class AppKitBackend: AppBackend {
908912
public func dismissAlert(_ alert: Alert, window: Window) {
909913
window.endSheet(alert.window)
910914
}
915+
916+
public func showOpenDialog(
917+
fileDialogOptions: FileDialogOptions,
918+
openDialogOptions: OpenDialogOptions,
919+
window: Window,
920+
resultHandler handleResult: @escaping (DialogResult<[URL]>) -> Void
921+
) {
922+
let panel = NSOpenPanel()
923+
panel.message = fileDialogOptions.title
924+
panel.prompt = fileDialogOptions.defaultButtonLabel
925+
panel.directoryURL = fileDialogOptions.initialDirectory
926+
panel.showsHiddenFiles = fileDialogOptions.showHiddenFiles
927+
panel.allowsOtherFileTypes = fileDialogOptions.allowOtherContentTypes
928+
929+
// TODO: allowedContentTypes
930+
931+
panel.allowsMultipleSelection = openDialogOptions.allowMultipleSelections
932+
panel.canChooseFiles = openDialogOptions.allowSelectingFiles
933+
panel.canChooseDirectories = openDialogOptions.allowSelectingDirectories
934+
935+
panel.beginSheetModal(for: window) { response in
936+
guard response != .continue else {
937+
return
938+
}
939+
940+
if response == .OK {
941+
handleResult(.success(panel.urls))
942+
} else {
943+
handleResult(.cancelled)
944+
}
945+
}
946+
}
947+
948+
public func showSaveDialog(
949+
fileDialogOptions: FileDialogOptions,
950+
saveDialogOptions: SaveDialogOptions,
951+
window: Window,
952+
resultHandler handleResult: @escaping (DialogResult<URL>) -> Void
953+
) {
954+
let panel = NSSavePanel()
955+
panel.message = fileDialogOptions.title
956+
panel.prompt = fileDialogOptions.defaultButtonLabel
957+
panel.directoryURL = fileDialogOptions.initialDirectory
958+
panel.showsHiddenFiles = fileDialogOptions.showHiddenFiles
959+
panel.allowsOtherFileTypes = fileDialogOptions.allowOtherContentTypes
960+
961+
// TODO: allowedContentTypes
962+
963+
panel.nameFieldLabel = saveDialogOptions.nameFieldLabel ?? panel.nameFieldLabel
964+
panel.nameFieldStringValue = saveDialogOptions.defaultFileName ?? ""
965+
966+
panel.beginSheetModal(for: window) { response in
967+
guard response != .continue else {
968+
return
969+
}
970+
971+
if response == .OK {
972+
handleResult(.success(panel.url!))
973+
} else {
974+
handleResult(.cancelled)
975+
}
976+
}
977+
}
911978
}
912979

913980
final class NSCustomMenuItem: NSMenuItem {

Sources/Gtk/Extensions/Widget+CastedPointer.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import Foundation
66

77
extension Widget {
8-
func castedPointer<T>() -> UnsafeMutablePointer<T>? {
9-
return widgetPointer?.cast()
8+
func castedPointer<T>() -> UnsafeMutablePointer<T> {
9+
return widgetPointer.cast()
1010
}
1111
}

Sources/Gtk/GObject.swift

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import CGtk
2+
3+
open class GObject: GObjectRepresentable {
4+
public var gobjectPointer: UnsafeMutablePointer<CGtk.GObject>
5+
6+
public var opaquePointer: OpaquePointer? {
7+
return OpaquePointer(gobjectPointer)
8+
}
9+
10+
public init<T>(_ pointer: UnsafeMutablePointer<T>?) {
11+
gobjectPointer = pointer!.cast()
12+
}
13+
14+
public init(_ pointer: OpaquePointer) {
15+
gobjectPointer = UnsafeMutablePointer(pointer)
16+
}
17+
18+
private var signals: [(UInt, Any)] = []
19+
20+
open func registerSignals() {}
21+
22+
func removeSignals() {
23+
for (handlerId, _) in signals {
24+
disconnectSignal(gobjectPointer, handlerId: handlerId)
25+
}
26+
27+
signals = []
28+
}
29+
30+
/// Adds a signal that is not carrying any additional information.
31+
func addSignal(name: String, callback: @escaping () -> Void) {
32+
let box = SignalBox0(callback: callback)
33+
let handler:
34+
@convention(c) (
35+
UnsafeMutableRawPointer, UnsafeMutableRawPointer
36+
) -> Void = { _, data in
37+
let box = Unmanaged<SignalBox0>.fromOpaque(data).takeUnretainedValue()
38+
box.callback()
39+
}
40+
41+
let handlerId = connectSignal(
42+
gobjectPointer,
43+
name: name,
44+
data: Unmanaged.passUnretained(box).toOpaque(),
45+
handler: unsafeBitCast(handler, to: GCallback.self)
46+
)
47+
48+
signals.append((handlerId, box))
49+
}
50+
51+
func addSignal<T1>(name: String, handler: GCallback, callback: @escaping (T1) -> Void) {
52+
let box = SignalBox1(callback: callback)
53+
54+
let handlerId = connectSignal(
55+
gobjectPointer,
56+
name: name,
57+
data: Unmanaged.passUnretained(box).toOpaque(),
58+
handler: handler
59+
)
60+
61+
signals.append((handlerId, box))
62+
}
63+
64+
func addSignal<T1, T2>(name: String, handler: GCallback, callback: @escaping (T1, T2) -> Void) {
65+
let box = SignalBox2(callback: callback)
66+
67+
let handlerId = connectSignal(
68+
gobjectPointer,
69+
name: name,
70+
data: Unmanaged.passUnretained(box).toOpaque(),
71+
handler: handler
72+
)
73+
74+
signals.append((handlerId, box))
75+
}
76+
77+
func addSignal<T1, T2, T3>(
78+
name: String, handler: GCallback, callback: @escaping (T1, T2, T3) -> Void
79+
) {
80+
let box = SignalBox3(callback: callback)
81+
82+
let handlerId = connectSignal(
83+
gobjectPointer,
84+
name: name,
85+
data: Unmanaged.passUnretained(box).toOpaque(),
86+
handler: handler
87+
)
88+
89+
signals.append((handlerId, box))
90+
}
91+
92+
func addSignal<T1, T2, T3, T4>(
93+
name: String, handler: GCallback, callback: @escaping (T1, T2, T3, T4) -> Void
94+
) {
95+
let box = SignalBox4(callback: callback)
96+
97+
let handlerId = connectSignal(
98+
gobjectPointer,
99+
name: name,
100+
data: Unmanaged.passUnretained(box).toOpaque(),
101+
handler: handler
102+
)
103+
104+
signals.append((handlerId, box))
105+
}
106+
107+
func addSignal<T1, T2, T3, T4, T5>(
108+
name: String, handler: GCallback, callback: @escaping (T1, T2, T3, T4, T5) -> Void
109+
) {
110+
let box = SignalBox5(callback: callback)
111+
112+
let handlerId = connectSignal(
113+
gobjectPointer,
114+
name: name,
115+
data: Unmanaged.passUnretained(box).toOpaque(),
116+
handler: handler
117+
)
118+
119+
signals.append((handlerId, box))
120+
}
121+
122+
func addSignal<T1, T2, T3, T4, T5, T6>(
123+
name: String, handler: GCallback, callback: @escaping (T1, T2, T3, T4, T5, T6) -> Void
124+
) {
125+
let box = SignalBox6(callback: callback)
126+
127+
let handlerId = connectSignal(
128+
gobjectPointer,
129+
name: name,
130+
data: Unmanaged.passUnretained(box).toOpaque(),
131+
handler: handler
132+
)
133+
134+
signals.append((handlerId, box))
135+
}
136+
}

0 commit comments

Comments
 (0)