Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions Sources/Gtk/Utility/GActionMap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,4 @@ extension GActionMap {
public func addAction(_ action: any GAction) {
g_action_map_add_action(actionMapPointer, action.actionPointer)
}

public func addAction(named name: String, action: @escaping () -> Void) {
let simpleAction = GSimpleAction(name: name, action: action)
addAction(simpleAction)
}
}
59 changes: 40 additions & 19 deletions Sources/Gtk/Utility/GSimpleAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,63 @@ public class GSimpleAction: GAction, GObjectRepresentable {

@GObjectProperty(named: "enabled") var enabled: Bool

private class Action {
var run: () -> Void

init(_ action: @escaping () -> Void) {
run = action
}
private class Box<T> {
var value: T
init(_ value: T) { self.value = value }
}

public init(name: String, action: @escaping () -> Void) {
actionPointer = g_simple_action_new(name, nil)

let wrappedAction = Action(action)
connectActionSignal(Box(action)) { _, _, data in
let action = Unmanaged<Box<() -> Void>>.fromOpaque(data).takeUnretainedValue()
action.value()
}
}

public init(name: String, state: Bool, action: @escaping (Bool) -> Void) {
actionPointer = g_simple_action_new_stateful(
name,
nil,
g_variant_new_boolean(state.toGBoolean())
)

connectActionSignal(Box(action)) { actionPointer, _, data in
// Get the current state...
let stateVariant = g_action_get_state(OpaquePointer(actionPointer))
let state = g_variant_get_boolean(stateVariant).toBool()

let handler:
// ...toggle it...
let newState = !state
g_simple_action_set_state(
OpaquePointer(actionPointer),
g_variant_new_boolean(newState.toGBoolean())
)

// ...then get the action and call it with the new state.
let action = Unmanaged<Box<(Bool) -> Void>>.fromOpaque(data).takeUnretainedValue()
action.value(newState)
}
}

private func connectActionSignal(
_ value: some AnyObject,
handler:
@convention(c) (
UnsafeMutableRawPointer,
OpaquePointer,
UnsafeMutableRawPointer
) -> Void =
{ _, _, data in
let action = Unmanaged<Action>.fromOpaque(data)
.takeUnretainedValue()
action.run()
}

) -> Void
) {
g_signal_connect_data(
UnsafeMutableRawPointer(actionPointer),
"activate",
gCallback(handler),
Unmanaged<Action>.passRetained(wrappedAction).toOpaque(),
Unmanaged.passRetained(value).toOpaque(),
{ data, _ in
Unmanaged<Action>.fromOpaque(data!).release()
Unmanaged<AnyObject>.fromOpaque(data!).release()
},
G_CONNECT_AFTER
)
}

public func registerSignals() {}
}
5 changes: 0 additions & 5 deletions Sources/Gtk3/Utility/GActionMap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,4 @@ extension GActionMap {
public func addAction(_ action: any GAction) {
g_action_map_add_action(actionMapPointer, action.actionPointer)
}

public func addAction(named name: String, action: @escaping () -> Void) {
let simpleAction = GSimpleAction(name: name, action: action)
addAction(simpleAction)
}
}
63 changes: 43 additions & 20 deletions Sources/Gtk3/Utility/GSimpleAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,61 @@ public class GSimpleAction: GAction, GObjectRepresentable {

@GObjectProperty(named: "enabled") var enabled: Bool

private class Action {
var run: () -> Void

init(_ action: @escaping () -> Void) {
run = action
}
private class Box<T> {
var value: T
init(_ value: T) { self.value = value }
}

public init(name: String, action: @escaping () -> Void) {
actionPointer = g_simple_action_new(name, nil)

let wrappedAction = Action(action)
connectActionSignal(Box(action)) { _, _, data in
let action = Unmanaged<Box<() -> Void>>.fromOpaque(data).takeUnretainedValue()
action.value()
}
}

public init(name: String, state: Bool, action: @escaping (Bool) -> Void) {
actionPointer = g_simple_action_new_stateful(
name,
nil,
g_variant_new_boolean(state.toGBoolean())
)

connectActionSignal(Box(action)) { actionPointer, _, data in
// Get the current state...
let stateVariant = g_action_get_state(OpaquePointer(actionPointer))
let state = g_variant_get_boolean(stateVariant).toBool()

// ...toggle it...
let newState = !state
g_simple_action_set_state(
OpaquePointer(actionPointer),
g_variant_new_boolean(newState.toGBoolean())
)

let handler:
@convention(c) (
UnsafeMutableRawPointer,
OpaquePointer,
UnsafeMutableRawPointer
) -> Void =
{ _, _, data in
let action = Unmanaged<Action>.fromOpaque(data)
.takeUnretainedValue()
action.run()
}
// ...then get the action and call it with the new state.
let action = Unmanaged<Box<(Bool) -> Void>>.fromOpaque(data).takeUnretainedValue()
action.value(newState)
}
}

private func connectActionSignal(
_ value: some AnyObject,
handler:
@convention(c) (
UnsafeMutableRawPointer,
OpaquePointer,
UnsafeMutableRawPointer
) -> Void
) {
g_signal_connect_data(
UnsafeMutableRawPointer(actionPointer),
"activate",
gCallback(handler),
Unmanaged<Action>.passRetained(wrappedAction).toOpaque(),
Unmanaged.passRetained(value).toOpaque(),
{ data, _ in
Unmanaged<Action>.fromOpaque(data!).release()
Unmanaged<AnyObject>.fromOpaque(data!).release()
},
G_CONNECT_AFTER
)
Expand Down
9 changes: 6 additions & 3 deletions Sources/Gtk3Backend/Gtk3Backend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,13 +240,16 @@ public final class Gtk3Backend: AppBackend {
switch item {
case .button(let label, let action):
if let action {
actionMap.addAction(named: actionName, action: action)
actionMap.addAction(GSimpleAction(name: actionName, action: action))
}

model.appendItem(label: label, actionName: "\(actionNamespace).\(actionName)")
case .toggle(let label, let value, let onChange):
// FIXME: Implement
logger.warning("menu toggles not implemented")
actionMap.addAction(
GSimpleAction(name: actionName, state: value, action: onChange)
)

model.appendItem(label: label, actionName: "\(actionNamespace).\(actionName)")
case .submenu(let submenu):
model.appendSubmenu(
label: submenu.label,
Expand Down
9 changes: 6 additions & 3 deletions Sources/GtkBackend/GtkBackend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@
return 0
#else
if window.showMenuBar {
// TODO: Don't hardcode this (if possible), because some Gtk

Check warning on line 166 in Sources/GtkBackend/GtkBackend.swift

View workflow job for this annotation

GitHub Actions / swift-lint

Todo Violation: TODOs should be resolved (Don't hardcode this (if possib...) (todo)
// themes may affect the height of the menu bar.
25
} else {
Expand All @@ -179,7 +179,7 @@
}

public func isWindowProgrammaticallyResizable(_ window: Window) -> Bool {
// TODO: Detect whether window is fullscreen

Check warning on line 182 in Sources/GtkBackend/GtkBackend.swift

View workflow job for this annotation

GitHub Actions / swift-lint

Todo Violation: TODOs should be resolved (Detect whether window is fulls...) (todo)
return true
}

Expand Down Expand Up @@ -275,13 +275,16 @@
switch item {
case .button(let label, let action):
if let action {
actionMap.addAction(named: actionName, action: action)
actionMap.addAction(GSimpleAction(name: actionName, action: action))
}

model.appendItem(label: label, actionName: "\(actionNamespace).\(actionName)")
case .toggle(let label, let value, let onChange):
// FIXME: Implement
logger.warning("menu toggles not implemented")
actionMap.addAction(
GSimpleAction(name: actionName, state: value, action: onChange)
)

model.appendItem(label: label, actionName: "\(actionNamespace).\(actionName)")
case .submenu(let submenu):
model.appendSubmenu(
label: submenu.label,
Expand Down Expand Up @@ -386,14 +389,14 @@
}

public func setRootEnvironmentChangeHandler(to action: @escaping () -> Void) {
// TODO: React to theme changes

Check warning on line 392 in Sources/GtkBackend/GtkBackend.swift

View workflow job for this annotation

GitHub Actions / swift-lint

Todo Violation: TODOs should be resolved (React to theme changes) (todo)
}

public func computeWindowEnvironment(
window: Window,
rootEnvironment: EnvironmentValues
) -> EnvironmentValues {
// TODO: Record window scale factor in here

Check warning on line 399 in Sources/GtkBackend/GtkBackend.swift

View workflow job for this annotation

GitHub Actions / swift-lint

Todo Violation: TODOs should be resolved (Record window scale factor in ...) (todo)
rootEnvironment
}

Expand All @@ -401,7 +404,7 @@
of window: Window,
to action: @escaping () -> Void
) {
// TODO: Notify when window scale factor changes

Check warning on line 407 in Sources/GtkBackend/GtkBackend.swift

View workflow job for this annotation

GitHub Actions / swift-lint

Todo Violation: TODOs should be resolved (Notify when window scale facto...) (todo)
}

public func setIncomingURLHandler(to action: @escaping (URL) -> Void) {
Expand Down Expand Up @@ -686,7 +689,7 @@

// private let tables = Tables()

// TODO: Implement tables

Check warning on line 692 in Sources/GtkBackend/GtkBackend.swift

View workflow job for this annotation

GitHub Actions / swift-lint

Todo Violation: TODOs should be resolved (Implement tables) (todo)
// public func createTable(rows: Int, columns: Int) -> Widget {
// let widget = Grid()

Expand Down Expand Up @@ -763,7 +766,7 @@
environment: EnvironmentValues,
action: @escaping () -> Void
) {
// TODO: Update button label color using environment

Check warning on line 769 in Sources/GtkBackend/GtkBackend.swift

View workflow job for this annotation

GitHub Actions / swift-lint

Todo Violation: TODOs should be resolved (Update button label color usin...) (todo)
let button = button as! Gtk.Button
button.sensitive = environment.isEnabled
button.label = label
Expand Down Expand Up @@ -1179,7 +1182,7 @@
}
},
window: window ?? windows[0]
) { result in

Check warning on line 1185 in Sources/GtkBackend/GtkBackend.swift

View workflow job for this annotation

GitHub Actions / swift-lint

Multiple Closures with Trailing Closure Violation: Trailing closure syntax should not be used when passing more than one closure argument (multiple_closures_with_trailing_closure)
switch result {
case .success(let urls):
handleResult(.success(urls[0]))
Expand Down Expand Up @@ -1212,7 +1215,7 @@
configure(chooser)

chooser.registerSignals()
chooser.response = { (_: NativeDialog, response: Int) -> Void in

Check warning on line 1218 in Sources/GtkBackend/GtkBackend.swift

View workflow job for this annotation

GitHub Actions / swift-lint

Redundant Void Return Violation: Returning Void in a function declaration is redundant (redundant_void_return)
// Release our intentional retain cycle which ironically only exists
// because of this line. The retain cycle keeps the file chooser
// around long enough for the user to respond (it gets released
Expand Down Expand Up @@ -1584,7 +1587,7 @@
}

public func createSheet(content: Widget) -> Sheet {
// TODO: dismissing a sheet with nested sheets doesn't trigger the onDismiss handlers of the nested sheets

Check warning on line 1590 in Sources/GtkBackend/GtkBackend.swift

View workflow job for this annotation

GitHub Actions / swift-lint

Todo Violation: TODOs should be resolved (dismissing a sheet with nested...) (todo)
// TODO: dismissing a sheet with nested sheets causes the app to freeze/deadlock or something along those lines...

let sheet = Sheet()
Expand Down
Loading