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)) 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 { extension ModifiedContent: ViewDeferredToRenderer where Content: View {
public var deferredBody: AnyView { public var deferredBody: AnyView {
if let widgetModifier = modifier as? WidgetModifier { guard let widgetModifier = modifier as? WidgetModifier else {
if let anyView = content as? ViewDeferredToRenderer, return AnyView(content)
let anyWidget = mapAnyView( }
anyView.deferredBody, let anyWidget: AnyWidget
transform: { (widget: AnyWidget) in widget } if let anyView = content as? ViewDeferredToRenderer,
) let _anyWidget = mapAnyView(
{ anyView.deferredBody,
return AnyView(WidgetView { transform: { (widget: AnyWidget) in widget }
let contentWidget = anyWidget.new($0) )
widgetModifier.modify(widget: contentWidget) {
return contentWidget anyWidget = _anyWidget
} content: { } else if let _anyWidget = content as? AnyWidget {
if let parentView = anyWidget as? ParentView { anyWidget = _anyWidget
ForEach(Array(parentView.children.enumerated()), id: \.offset) { _, view in } else {
view return AnyView(content)
} }
} return AnyView(WidgetView {
}) let contentWidget = anyWidget.new($0)
} else if let anyWidget = content as? AnyWidget { widgetModifier.modify(widget: contentWidget)
return AnyView(WidgetView { return contentWidget
let contentWidget = anyWidget.new($0) }
widgetModifier.modify(widget: contentWidget) update: { widget in
return contentWidget anyWidget.update(widget: widget)
} content: {
if let parentView = anyWidget as? ParentView { // Is it correct to apply the modifier again after updating?
ForEach(Array(parentView.children.enumerated()), id: \.offset) { _, view in // I assume so since the modifier parameters may have changed.
view if case .widget(let w) = widget.storage {
} widgetModifier.modify(widget: w)
}
})
} }
} }
return AnyView(content) 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 { struct WidgetView<Content: View>: View, AnyWidget, ParentView {
let build: (UnsafeMutablePointer<GtkApplication>) -> UnsafeMutablePointer<GtkWidget> let build: (UnsafeMutablePointer<GtkApplication>) -> UnsafeMutablePointer<GtkWidget>
let update: (Widget) -> Void
let content: Content let content: Content
let expand: Bool let expand: Bool
init(build: @escaping (UnsafeMutablePointer<GtkApplication>) -> UnsafeMutablePointer<GtkWidget>, init(build: @escaping (UnsafeMutablePointer<GtkApplication>) -> UnsafeMutablePointer<GtkWidget>,
update: @escaping (Widget) -> Void = { _ in },
expand: Bool = false, expand: Bool = false,
@ViewBuilder content: () -> Content) @ViewBuilder content: () -> Content)
{ {
self.build = build self.build = build
self.expand = expand self.expand = expand
self.content = content() self.content = content()
self.update = update
} }
func new(_ application: UnsafeMutablePointer<GtkApplication>) -> UnsafeMutablePointer<GtkWidget> { func new(_ application: UnsafeMutablePointer<GtkApplication>) -> UnsafeMutablePointer<GtkWidget> {
@ -44,9 +47,8 @@ struct WidgetView<Content: View>: View, AnyWidget, ParentView {
} }
func update(widget: Widget) { func update(widget: Widget) {
// Rebuild from scratch if case .widget = widget.storage {
if case let .widget(w) = widget.storage { update(widget)
widget.destroy()
} }
} }

View File

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