GTK: `background` modifier, support widget updates in `WidgetView` (#344)

Re-created #340.

* Added background modifier support for color backgrounds

* Fix indentation

* Allow WidgetView to be initialized with an update closure in order to fix updates to children of WidgetViews

* Fix indentation

Co-authored-by: Morten Bek Ditlevsen <morten@ka-ching.dk>
This commit is contained in:
Max Desiatov 2020-12-26 19:22:16 +00:00 committed by GitHub
parent 6ef59293f5
commit c9877dcbd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 35 deletions

View File

@ -43,3 +43,14 @@ extension _FlexFrameLayout: WidgetModifier {
gtk_widget_set_size_request(widget, Int32(idealWidth ?? -1), Int32(idealHeight ?? -1))
}
}
extension _BackgroundModifier: WidgetModifier where Background == Color {
public func modify(widget: UnsafeMutablePointer<GtkWidget>) {
let resolved = _ColorProxy(self.background).resolve(in: environment)
var color = GdkRGBA(red: resolved.red,
green: resolved.green,
blue: resolved.blue,
alpha: resolved.opacity)
gtk_widget_override_background_color(widget, GtkStateFlags(rawValue: 0), &color)
}
}

View File

@ -24,38 +24,44 @@ protocol WidgetModifier {
extension ModifiedContent: ViewDeferredToRenderer where Content: View {
public var deferredBody: AnyView {
if let widgetModifier = modifier as? WidgetModifier {
guard let widgetModifier = modifier as? WidgetModifier else {
return AnyView(content)
}
let anyWidget: AnyWidget
if let anyView = content as? ViewDeferredToRenderer,
let anyWidget = mapAnyView(
let _anyWidget = mapAnyView(
anyView.deferredBody,
transform: { (widget: AnyWidget) in widget }
)
{
return AnyView(WidgetView {
let contentWidget = anyWidget.new($0)
widgetModifier.modify(widget: contentWidget)
return contentWidget
} content: {
if let parentView = anyWidget as? ParentView {
ForEach(Array(parentView.children.enumerated()), id: \.offset) { _, view in
view
}
}
})
} else if let anyWidget = content as? AnyWidget {
return AnyView(WidgetView {
let contentWidget = anyWidget.new($0)
widgetModifier.modify(widget: contentWidget)
return contentWidget
} content: {
if let parentView = anyWidget as? ParentView {
ForEach(Array(parentView.children.enumerated()), id: \.offset) { _, view in
view
}
}
})
}
}
anyWidget = _anyWidget
} else if let _anyWidget = content as? AnyWidget {
anyWidget = _anyWidget
} else {
return AnyView(content)
}
return AnyView(WidgetView {
let contentWidget = anyWidget.new($0)
widgetModifier.modify(widget: contentWidget)
return contentWidget
}
update: { widget in
anyWidget.update(widget: widget)
// Is it correct to apply the modifier again after updating?
// I assume so since the modifier parameters may have changed.
if case .widget(let w) = widget.storage {
widgetModifier.modify(widget: w)
}
}
content: {
if let parentView = anyWidget as? ParentView, parentView.children.count > 1 {
ForEach(Array(parentView.children.enumerated()), id: \.offset) { _, view in
view
}
} else if let parentView = anyWidget as? ParentView, parentView.children.count == 1 {
parentView.children[0]
}
})
}
}

View File

@ -27,16 +27,19 @@ extension AnyWidget {
struct WidgetView<Content: View>: View, AnyWidget, ParentView {
let build: (UnsafeMutablePointer<GtkApplication>) -> UnsafeMutablePointer<GtkWidget>
let update: (Widget) -> Void
let content: Content
let expand: Bool
init(build: @escaping (UnsafeMutablePointer<GtkApplication>) -> UnsafeMutablePointer<GtkWidget>,
update: @escaping (Widget) -> Void = { _ in },
expand: Bool = false,
@ViewBuilder content: () -> Content)
{
self.build = build
self.expand = expand
self.content = content()
self.update = update
}
func new(_ application: UnsafeMutablePointer<GtkApplication>) -> UnsafeMutablePointer<GtkWidget> {
@ -44,9 +47,8 @@ struct WidgetView<Content: View>: View, AnyWidget, ParentView {
}
func update(widget: Widget) {
// Rebuild from scratch
if case let .widget(w) = widget.storage {
widget.destroy()
if case .widget = widget.storage {
update(widget)
}
}

View File

@ -22,7 +22,7 @@ struct Counter: View {
@State private var count: Int = 0
var body: some View {
VStack {
Text("\(count)")
Text("\(count)").background(Color(red: 0.5, green: 1, blue: 0.5))
HStack {
Button("Decrement") { count -= 1 }
Button("Increment") { count += 1 }