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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ DerivedData/
.swiftpm
vcpkg_installed/
*.trace
notes.json
5 changes: 5 additions & 0 deletions Examples/Bundler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,8 @@ version = '0.1.0'
identifier = 'dev.swiftcrossui.HoverExample'
product = 'HoverExample'
version = '0.1.0'

[apps.ForEachExample]
identifier = 'dev.swiftcrossui.ForEachExample'
product = 'ForEachExample'
version = '0.1.0'
6 changes: 3 additions & 3 deletions Examples/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Examples/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ let package = Package(
.executableTarget(
name: "HoverExample",
dependencies: exampleDependencies
)
),
.executableTarget(
name: "ForEachExample",
dependencies: exampleDependencies
)
]
)
5 changes: 0 additions & 5 deletions Examples/Sources/CounterExample/CounterApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ struct CounterApp: App {
count -= 1
}
Text("Count: \(count)")
.overlay {
GeometryReader { proxy in
Color.blue.opacity(0.5)
}
}
Button("+") {
count += 1
}
Expand Down
96 changes: 96 additions & 0 deletions Examples/Sources/ForEachExample/ForEachApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import DefaultBackend
import Foundation
import SwiftCrossUI

#if canImport(SwiftBundlerRuntime)
import SwiftBundlerRuntime
#endif

@main
@HotReloadable
struct ForEachApp: App {
@State var items = (0..<20).map { Item("\($0)") }
@State var biggestValue = 19
@State var insertionPosition = 10

var body: some Scene {
WindowGroup("ForEach") {
#hotReloadable {
VStack {
VStack {
Button("Append") {
biggestValue += 1
items.append(.init("\(biggestValue)"))
}

#if !os(tvOS)
Button(
"Insert before item at position \(insertionPosition)"
) {
biggestValue += 1
items.insert(.init("\(biggestValue)"), at: insertionPosition)
}

Slider(value: $insertionPosition, in: 0...(items.count - 1))
.onChange(of: items.count) {
let upperLimit = max(items.count - 1, 0)
insertionPosition = min(insertionPosition, upperLimit)
}
.frame(maxWidth: 200)
#endif
}
.padding(10)

ScrollView {
ForEach(Array(items.enumerated()), id: \.element.id) { (index, item) in
ItemRow(
item: item,
isFirst: index == 0,
isLast: index == items.count - 1
) {
items.remove(at: index)
} moveUp: {
guard index != items.startIndex else { return }
items.swapAt(index, index - 1)
} moveDown: {
guard index != items.endIndex else { return }
items.swapAt(index, index + 1)
}
}
.frame(maxWidth: .infinity)
}
}
}
}
.defaultSize(width: 400, height: 800)
}
}

struct ItemRow: View {
var item: Item
let isFirst: Bool
let isLast: Bool
var remove: () -> Void
var moveUp: () -> Void
var moveDown: () -> Void

var body: some View {
HStack {
Text(item.value)
Button("Delete") { remove() }
Button("⌃") { moveUp() }
.disabled(isFirst)
Button("⌄") { moveDown() }
.disabled(isLast)
}
}
}

struct Item: Identifiable {
let id = UUID()
var value: String

init(_ value: String) {
self.value = value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ struct GreetingGeneratorApp: App {
.padding(.top, 20)

ScrollView {
ForEach(greetings.reversed()[1...]) { greeting in
ForEach(greetings.reversed()[1...], id: \.self) { greeting in
Text(greeting)
}
}
Expand Down
3 changes: 1 addition & 2 deletions Examples/Sources/StressTestExample/StressTestApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,11 @@ struct StressTestApp: App {
for _ in 0..<1000 {
values.append(Self.options.randomElement()!)
}

self.values[tab!] = values
}
if let values = values[tab!] {
ScrollView {
ForEach(values) { value in
ForEach(values, id: \.self) { value in
Text(value)
}
}.frame(minWidth: 300)
Expand Down
12 changes: 8 additions & 4 deletions Examples/Sources/WebViewExample/WebViewApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,14 @@ struct WebViewApp: App {
}
.padding()

WebView($url)
.onChange(of: url) {
urlInput = url.absoluteString
}
#if !os(tvOS)
WebView($url)
.onChange(of: url) {
urlInput = url.absoluteString
}
#else
Text("WebView isn't supported on tvOS")
#endif
}
}
}
Expand Down
13 changes: 11 additions & 2 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ let package = Package(
url: "https://github.com/stackotter/swift-winui",
revision: "1695ee3ea2b7a249f6504c7f1759e7ec7a38eb86"
),
.package(
url: "https://github.com/apple/swift-collections.git",
.upToNextMinor(from: "1.2.1")
),
.package(
url: "https://github.com/stackotter/swift-benchmark",
.upToNextMinor(from: "0.2.0")
Expand Down Expand Up @@ -150,6 +154,7 @@ let package = Package(
"HotReloadingMacrosPlugin",
.product(name: "ImageFormats", package: "swift-image-formats"),
.product(name: "Logging", package: "swift-log"),
.product(name: "OrderedCollections", package: "swift-collections"),
],
exclude: [
"Builders/ViewBuilder.swift.gyb",
Expand Down
10 changes: 5 additions & 5 deletions Sources/SwiftCrossUI/Builders/MenuItemsBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public struct MenuItemsBuilder {
public static func buildBlock() -> [MenuItem] {
[]
}

public static func buildPartialBlock(first: Button) -> [MenuItem] {
[.button(first)]
}
Expand All @@ -21,8 +21,8 @@ public struct MenuItemsBuilder {
first.items
}

public static func buildPartialBlock<Items: Collection>(
first: ForEach<Items, [MenuItem]>
public static func buildPartialBlock<Items: Collection, ID: Hashable>(
first: ForEach<Items, ID, [MenuItem]>
) -> [MenuItem] {
first.elements.map(first.child).flatMap { $0 }
}
Expand Down Expand Up @@ -55,9 +55,9 @@ public struct MenuItemsBuilder {
accumulated + buildPartialBlock(first: next)
}

public static func buildPartialBlock<Items: Collection>(
public static func buildPartialBlock<Items: Collection, ID: Hashable>(
accumulated: [MenuItem],
next: ForEach<Items, [MenuItem]>
next: ForEach<Items, ID, [MenuItem]>
) -> [MenuItem] {
accumulated + buildPartialBlock(first: next)
}
Expand Down
10 changes: 6 additions & 4 deletions Sources/SwiftCrossUI/Environment/Actions/OpenURLAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ public struct OpenURLAction {
do {
try backend.openExternalURL(url)
} catch {
logger.warning("failed to open external url", metadata: [
"url": "\(url)",
"error": "\(error)",
])
logger.warning(
"failed to open external url",
metadata: [
"url": "\(url)",
"error": "\(error)",
])
}
}
}
Expand Down
10 changes: 6 additions & 4 deletions Sources/SwiftCrossUI/Environment/Actions/RevealFileAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ public struct RevealFileAction {
do {
try backend.revealFile(file)
} catch {
logger.warning("failed to reveal file", metadata: [
"url": "\(file)",
"error": "\(error)",
])
logger.warning(
"failed to reveal file",
metadata: [
"url": "\(file)",
"error": "\(error)",
])
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion Sources/SwiftCrossUI/Layout/LayoutSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ public enum LayoutSystem {
var tag: String?

public init(
computeLayout: @escaping @MainActor (ProposedViewSize, EnvironmentValues) ->
computeLayout:
@escaping @MainActor (ProposedViewSize, EnvironmentValues) ->
ViewLayoutResult,
commit: @escaping @MainActor () -> ViewLayoutResult,
tag: String? = nil
Expand Down
3 changes: 1 addition & 2 deletions Sources/SwiftCrossUI/Views/Button.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ public struct Button: Sendable {
}
}

extension Button: View {
}
extension Button: View {}

extension Button: ElementaryView {
public func asWidget<Backend: AppBackend>(backend: Backend) -> Backend.Widget {
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftCrossUI/Views/EitherView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ extension EitherView: TypeSafeView {
switch storage {
case .a(let a):
switch children.node {
case let .a(nodeA):
case .a(let nodeA):
result = nodeA.computeLayout(
with: a,
proposedSize: proposedSize,
Expand All @@ -82,7 +82,7 @@ extension EitherView: TypeSafeView {
}
case .b(let b):
switch children.node {
case let .b(nodeB):
case .b(let nodeB):
result = nodeB.computeLayout(
with: b,
proposedSize: proposedSize,
Expand Down
Loading
Loading