Skip to content

Commit

Permalink
Impl WinUIBackend ScrollView, fix ScrollView layout calculations
Browse files Browse the repository at this point in the history
ScrollView had some layout bugs affecting all backends.
  • Loading branch information
stackotter committed Jan 8, 2025
1 parent 9961828 commit baf48b3
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 110 deletions.
77 changes: 2 additions & 75 deletions Examples/Sources/NotesExample/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,79 +28,6 @@ class NotesState: Observable, Codable {
var error: String?
}

struct OnChangeOfView<Value: Equatable, Child: View>: View {
var state = OnChangeOfViewState()

var currentValue: Value
var onChange: (Value) -> Void
var child: Child
var notifyInitial: Bool

class OnChangeOfViewState: Observable {
var value: Value?
}

init(
_ value: Value,
_ onChange: @escaping (Value) -> Void,
_ notifyInitial: Bool,
_ child: Child
) {
self.currentValue = value
self.onChange = onChange
self.notifyInitial = notifyInitial
self.child = child
}

var body: some View {
if state.value != currentValue {
let isInitial = state.value != nil
state.value = currentValue
if !isInitial || notifyInitial {
onChange(currentValue)
}
}
return child
}
}

struct OnAppearView<Child: View>: View {
var state = OnAppearViewState()
var onAppear: () -> Void
var child: Child

class OnAppearViewState: Observable {
var hasAppeared = false
}

init(_ onAppear: @escaping () -> Void, _ child: Child) {
self.onAppear = onAppear
self.child = child
}

var body: some View {
if !state.hasAppeared {
state.hasAppeared = true
onAppear()
}
return child
}
}

extension View {
func onChange<Value>(
of value: Value,
initial: Bool = false,
_ action: @escaping (Value) -> Void
) -> OnChangeOfView<Value, Self> {
OnChangeOfView(value, action, initial, self)
}

func onAppear(perform action: @escaping () -> Void) -> OnAppearView<Self> {
OnAppearView(action, self)
}
}

struct ContentView: View {
let notesFile = URL(fileURLWithPath: "notes.json")

Expand Down Expand Up @@ -149,9 +76,9 @@ struct ContentView: View {
state.selectedNoteId = note.id
}
}
.onChange(of: state.notes) { notes in
.onChange(of: state.notes) {
do {
let data = try JSONEncoder().encode(notes)
let data = try JSONEncoder().encode(state.notes)
try data.write(to: notesFile)
} catch {
print("Error: \(error)")
Expand Down
5 changes: 4 additions & 1 deletion Examples/Sources/NotesExample/NotesApp.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import DefaultBackend
import SwiftBundlerRuntime
import SwiftCrossUI

#if canImport(SwiftBundlerRuntime)
import SwiftBundlerRuntime
#endif

@main
@HotReloadable
struct NotesApp: App {
Expand Down
1 change: 1 addition & 0 deletions Examples/notes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"content":"Welcome SwiftCrossNotes!","id":"D70F96BB-2153-4FC6-A79F-FD231B4F3523","title":"Hello, world!"},{"content":"- Carrots","id":"04864C37-38E3-4FC1-8BAF-C2BA41AAF8D9","title":"Shopping list"},{"title":"My note","id":"EAA92A96-E443-4374-AB35-41CD4BD99EBD","content":"I hope for world piss"}]
16 changes: 6 additions & 10 deletions Sources/SwiftCrossUI/Views/ScrollView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,14 @@ public struct ScrollView<Content: View>: TypeSafeView, View {
scrollViewWidth = max(proposedSize.x, verticalScrollBarWidth)
minimumWidth = verticalScrollBarWidth
} else {
scrollViewWidth = max(
contentSize.size.x,
contentSize.minimumWidth + verticalScrollBarWidth
)
scrollViewWidth = contentSize.size.x + verticalScrollBarWidth
minimumWidth = contentSize.minimumWidth + verticalScrollBarWidth
}
if axes.contains(.vertical) {
scrollViewHeight = max(proposedSize.y, horizontalScrollBarHeight)
minimumHeight = horizontalScrollBarHeight
} else {
scrollViewHeight = max(
contentSize.size.y,
contentSize.minimumHeight + horizontalScrollBarHeight
)
scrollViewHeight = contentSize.size.y + horizontalScrollBarHeight
minimumHeight = contentSize.minimumHeight + horizontalScrollBarHeight
}

Expand All @@ -102,9 +96,11 @@ public struct ScrollView<Content: View>: TypeSafeView, View {
if !dryRun {
let proposedContentSize = SIMD2(
hasHorizontalScrollBar
? contentSize.idealSize.x : (contentSize.size.x - verticalScrollBarWidth),
? contentSize.idealSize.x
: min(contentSize.size.x, proposedSize.x - verticalScrollBarWidth),
hasVerticalScrollBar
? contentSize.idealSize.y : (contentSize.size.y - horizontalScrollBarHeight)
? contentSize.idealSize.y
: min(contentSize.size.y, proposedSize.y - horizontalScrollBarHeight)
)

finalResult = children.child.update(
Expand Down
68 changes: 44 additions & 24 deletions Sources/WinUIBackend/WinUIBackend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public struct WinUIBackend: AppBackend {
public let defaultPaddingAmount = 10

public var scrollBarWidth: Int {
fatalError("TODO")
12
}

private class InternalState {
Expand All @@ -61,8 +61,9 @@ public struct WinUIBackend: AppBackend {
// Toggle Switch has annoying default 'internal margins' (not Control
// margins that we can set directly) that we can luckily get rid of by
// overriding the relevant resource values.
application.resources.insert("ToggleSwitchPreContentMargin", 0.0 as Double)
application.resources.insert("ToggleSwitchPostContentMargin", 0.0 as Double)
_ = application.resources.insert("ToggleSwitchPreContentMargin", 0.0 as Double)
_ = application.resources.insert("ToggleSwitchPostContentMargin", 0.0 as Double)

callback()
}
WinUIApplication.main()
Expand Down Expand Up @@ -219,7 +220,6 @@ public struct WinUIBackend: AppBackend {
public func addChild(_ child: Widget, to container: Widget) {
let container = container as! Canvas
container.children.append(child)
try! container.updateLayout()
}

public func setPosition(ofChildAt index: Int, in container: Widget, to position: SIMD2<Int>) {
Expand Down Expand Up @@ -394,12 +394,10 @@ public struct WinUIBackend: AppBackend {
let block = createTextView()
updateTextView(block, content: text, environment: environment)

let allocation =
proposedFrame.map(WindowsFoundation.Size.init(_:))
?? WindowsFoundation.Size(
width: .infinity,
height: .infinity
)
let allocation = WindowsFoundation.Size(
width: (proposedFrame?.x).map(Float.init(_:)) ?? .infinity,
height: .infinity
)
try! block.measure(allocation)

let computedSize = block.desiredSize
Expand Down Expand Up @@ -451,11 +449,42 @@ public struct WinUIBackend: AppBackend {
internalState.buttonClickActions[ObjectIdentifier(button)] = action
}

// public func createScrollContainer(for child: Widget) -> Widget {
// let scrollViewer = ScrollViewer()
// scrollViewer.content = child
// return scrollViewer
// }
public func createScrollContainer(for child: Widget) -> Widget {
let scrollViewer = WinUI.ScrollViewer()
scrollViewer.content = child
child.horizontalAlignment =
__x_ABI_CMicrosoft_CUI_CXaml_CHorizontalAlignment_Left
child.verticalAlignment = __x_ABI_CMicrosoft_CUI_CXaml_CVerticalAlignment_Top
return scrollViewer
}

public func setScrollBarPresence(
ofScrollContainer scrollView: Widget,
hasVerticalScrollBar: Bool,
hasHorizontalScrollBar: Bool
) {
let scrollViewer = scrollView as! WinUI.ScrollViewer

scrollViewer.isHorizontalRailEnabled = hasHorizontalScrollBar
scrollViewer.horizontalScrollMode =
hasHorizontalScrollBar
? __x_ABI_CMicrosoft_CUI_CXaml_CControls_CScrollMode_Enabled
: __x_ABI_CMicrosoft_CUI_CXaml_CControls_CScrollMode_Disabled
scrollViewer.horizontalScrollBarVisibility =
hasHorizontalScrollBar
? __x_ABI_CMicrosoft_CUI_CXaml_CControls_CScrollBarVisibility_Visible
: __x_ABI_CMicrosoft_CUI_CXaml_CControls_CScrollBarVisibility_Hidden

scrollViewer.isVerticalRailEnabled = hasVerticalScrollBar
scrollViewer.verticalScrollMode =
hasVerticalScrollBar
? __x_ABI_CMicrosoft_CUI_CXaml_CControls_CScrollMode_Enabled
: __x_ABI_CMicrosoft_CUI_CXaml_CControls_CScrollMode_Disabled
scrollViewer.verticalScrollBarVisibility =
hasVerticalScrollBar
? __x_ABI_CMicrosoft_CUI_CXaml_CControls_CScrollBarVisibility_Visible
: __x_ABI_CMicrosoft_CUI_CXaml_CControls_CScrollBarVisibility_Hidden
}

public func createSlider() -> Widget {
let slider = Slider()
Expand Down Expand Up @@ -847,12 +876,3 @@ final class CustomComboBox: ComboBox {
final class CustomSplitView: SplitView {
var sidebarResizeHandler: (() -> Void)?
}

extension WindowsFoundation.Size {
init(_ other: SIMD2<Int>) {
self.init(
width: Float(other.x),
height: Float(other.y)
)
}
}

0 comments on commit baf48b3

Please sign in to comment.