diff --git a/Demo/App.swiftpm/Sources/Catalog.swift b/Demo/App.swiftpm/Sources/Catalog.swift new file mode 100644 index 0000000..b758bff --- /dev/null +++ b/Demo/App.swiftpm/Sources/Catalog.swift @@ -0,0 +1,49 @@ +#if canImport(AndroidSwiftUI) +import AndroidSwiftUI +#else +import SwiftUI +#endif + +/// A component catalog: one playground per supported SwiftUI feature, navigated +/// with a `NavigationStack` and a lazy `List`. +struct ContentView: View { + + var body: some View { + NavigationStack { + List(CatalogEntry.all) { entry in + NavigationLink(entry.title, destination: entry.screen) + } + .navigationTitle("Catalog") + } + } +} + +struct CatalogEntry: Identifiable { + let id: String + let title: String + let screen: AnyCatalogScreen + + static let all: [CatalogEntry] = [ + CatalogEntry(id: "text", title: "Text", screen: AnyCatalogScreen(TextPlayground())), + CatalogEntry(id: "button", title: "Button", screen: AnyCatalogScreen(ButtonPlayground())), + CatalogEntry(id: "toggle", title: "Toggle", screen: AnyCatalogScreen(TogglePlayground())), + CatalogEntry(id: "slider", title: "Slider", screen: AnyCatalogScreen(SliderPlayground())), + CatalogEntry(id: "textfield", title: "TextField", screen: AnyCatalogScreen(TextFieldPlayground())), + CatalogEntry(id: "picker", title: "Picker", screen: AnyCatalogScreen(PickerPlayground())), + CatalogEntry(id: "progress", title: "ProgressView", screen: AnyCatalogScreen(ProgressViewPlayground())), + CatalogEntry(id: "stack", title: "Stacks", screen: AnyCatalogScreen(StackPlayground())), + CatalogEntry(id: "spacer", title: "Spacer & Divider", screen: AnyCatalogScreen(SpacerDividerPlayground())), + CatalogEntry(id: "color", title: "Color", screen: AnyCatalogScreen(ColorPlayground())), + CatalogEntry(id: "scroll", title: "ScrollView", screen: AnyCatalogScreen(ScrollViewPlayground())), + CatalogEntry(id: "list", title: "List", screen: AnyCatalogScreen(ListPlayground())), + CatalogEntry(id: "grid", title: "Grid", screen: AnyCatalogScreen(GridPlayground())), + CatalogEntry(id: "modifier", title: "Modifiers", screen: AnyCatalogScreen(ModifierPlayground())), + CatalogEntry(id: "navigation", title: "Navigation", screen: AnyCatalogScreen(NavigationPlayground())), + CatalogEntry(id: "tab", title: "TabView", screen: AnyCatalogScreen(TabViewPlayground())), + CatalogEntry(id: "sheet", title: "Sheet", screen: AnyCatalogScreen(SheetPlayground())), + CatalogEntry(id: "alert", title: "Alert", screen: AnyCatalogScreen(AlertPlayground())), + CatalogEntry(id: "state", title: "State", screen: AnyCatalogScreen(StatePlayground())), + CatalogEntry(id: "environment", title: "Environment", screen: AnyCatalogScreen(EnvironmentPlayground())), + CatalogEntry(id: "observable", title: "Observable", screen: AnyCatalogScreen(ObservablePlayground())), + ] +} diff --git a/Demo/App.swiftpm/Sources/CatalogSupport.swift b/Demo/App.swiftpm/Sources/CatalogSupport.swift new file mode 100644 index 0000000..ee59a36 --- /dev/null +++ b/Demo/App.swiftpm/Sources/CatalogSupport.swift @@ -0,0 +1,31 @@ +#if canImport(AndroidSwiftUI) +import AndroidSwiftUI +#else +import SwiftUI +#endif + +/// Type-erased catalog screen (a small AnyView shim usable as a navigation +/// destination while keeping the entry list homogeneous). +struct AnyCatalogScreen: View { + private let content: AnyView + init(_ view: V) { self.content = AnyView(view) } + var body: some View { content } +} + +/// A titled example row shared by the playgrounds. +struct Example: View { + let title: String + let content: Content + init(_ title: String, @ViewBuilder content: () -> Content) { + self.title = title + self.content = content() + } + var body: some View { + VStack(alignment: .leading, spacing: 8) { + Text(title) + content + Divider() + } + .padding() + } +} diff --git a/Demo/App.swiftpm/Sources/ContainerPlaygrounds.swift b/Demo/App.swiftpm/Sources/ContainerPlaygrounds.swift new file mode 100644 index 0000000..5c82ef4 --- /dev/null +++ b/Demo/App.swiftpm/Sources/ContainerPlaygrounds.swift @@ -0,0 +1,71 @@ +#if canImport(AndroidSwiftUI) +import AndroidSwiftUI +#else +import SwiftUI +#endif + +struct ListPlayground: View { + @State private var rows = (1...25).map(CatalogRow.init) + var body: some View { + List(rows) { row in + Text(row.title) + } + .refreshable { + try? await Task.sleep(for: .seconds(1)) + rows.insert(CatalogRow(id: rows.count + 1), at: 0) + } + } +} + +struct CatalogRow: Identifiable { + let id: Int + var title: String { "Row \(id)" } +} + +struct GridPlayground: View { + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Example("Three fixed columns") { + LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())]) { + ForEach(1...9, id: \.self) { n in + Text("\(n)").padding().background(Color.blue).cornerRadius(6) + } + } + } + Example("Adaptive columns") { + LazyVGrid(columns: [GridItem(.adaptive(minimum: 80))]) { + ForEach(1...8, id: \.self) { n in + Text("Item \(n)").padding().background(Color.orange).cornerRadius(6) + } + } + } + Example("Two rows, horizontal") { + LazyHGrid(rows: [GridItem(.flexible()), GridItem(.flexible())]) { + ForEach(1...8, id: \.self) { n in + Text("Cell \(n)").padding().background(Color.green).cornerRadius(6) + } + } + .frame(height: 160) + } + } + } + } +} + +struct ModifierPlayground: View { + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Example("Padding") { Text("Padded").padding().background(Color.blue) } + Example("Frame 200x60") { Text("Fixed").frame(width: 200, height: 60).background(Color.green) } + Example("Background") { Text("Colored").padding().background(Color.orange) } + Example("Corner radius") { Text("Rounded").padding().background(Color.purple).cornerRadius(16) } + Example("Offset") { Text("Shifted").offset(x: 30, y: 0).background(Color.red) } + Example("Rotation") { Text("Rotated").padding().background(Color.yellow).rotationEffect(.degrees(15)) } + Example("Scale") { Text("Scaled").padding().background(Color.pink).scaleEffect(1.4) } + Example("Opacity") { Text("Faded").padding().background(Color.blue).opacity(0.4) } + } + } + } +} diff --git a/Demo/App.swiftpm/Sources/ControlPlaygrounds.swift b/Demo/App.swiftpm/Sources/ControlPlaygrounds.swift new file mode 100644 index 0000000..c1eaf0e --- /dev/null +++ b/Demo/App.swiftpm/Sources/ControlPlaygrounds.swift @@ -0,0 +1,170 @@ +#if canImport(AndroidSwiftUI) +import AndroidSwiftUI +#else +import SwiftUI +#endif + +struct TextPlayground: View { + @State private var counter = 0 + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Example("Plain") { Text("Hello, world") } + Example("Verbatim") { Text(verbatim: "Raw string, no interpolation") } + Example("Interpolated") { Text("Counter is \(counter)") } + Example("Bump the counter") { Button("Increment") { counter += 1 } } + Example("Multiline") { + Text("A longer passage of text that wraps onto multiple lines when it no longer fits within the width of the screen.") + } + Example("Styled") { + Text("Blue on a rounded chip") + .padding() + .background(Color.blue) + .cornerRadius(12) + } + } + } + } +} + +struct ButtonPlayground: View { + @State private var taps = 0 + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Example("Taps: \(taps)") { Text("Tap any button below") } + Example("Title initializer") { Button("Tap me") { taps += 1 } } + Example("Label closure") { + Button(action: { taps += 1 }) { Text("Custom label") } + } + Example("Styled label") { + Button(action: { taps += 1 }) { + Text("Padded label") + .padding() + .background(Color.green) + .cornerRadius(8) + } + } + Example("Reset") { Button("Reset to zero") { taps = 0 } } + } + } + } +} + +struct TogglePlayground: View { + @State private var a = false + @State private var b = true + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Example("Off by default") { + VStack(alignment: .leading, spacing: 8) { + Toggle("Enable feature", isOn: $a) + Text(a ? "Feature is on" : "Feature is off") + } + } + Example("On by default") { + VStack(alignment: .leading, spacing: 8) { + Toggle("Notifications", isOn: $b) + Text(b ? "Notifications on" : "Notifications off") + } + } + } + } + } +} + +struct SliderPlayground: View { + @State private var unit = 0.5 + @State private var wide = 25.0 + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Example("0...1") { + VStack(alignment: .leading, spacing: 8) { + Slider(value: $unit, in: 0...1) + Text("Value: \(Int(unit * 100))%") + } + } + Example("0...100") { + VStack(alignment: .leading, spacing: 8) { + Slider(value: $wide, in: 0...100) + Text("Value: \(Int(wide))") + } + } + } + } + } +} + +struct TextFieldPlayground: View { + @State private var name = "" + @State private var city = "" + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Example("Name") { + VStack(alignment: .leading, spacing: 8) { + TextField("Your name", text: $name) + Text(name.isEmpty ? "Nothing typed yet" : "Hello, \(name)") + } + } + Example("City") { + VStack(alignment: .leading, spacing: 8) { + TextField("Your city", text: $city) + Text(city.isEmpty ? "—" : "You are in \(city)") + } + } + } + } + } +} + +struct PickerPlayground: View { + @State private var fruit = "Apple" + @State private var size = 1 + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Example("String selection") { + VStack(alignment: .leading, spacing: 8) { + Picker("Fruit", selection: $fruit) { + Text("Apple").tag("Apple") + Text("Banana").tag("Banana") + Text("Cherry").tag("Cherry") + } + Text("Selected: \(fruit)") + } + } + Example("Int selection") { + VStack(alignment: .leading, spacing: 8) { + Picker("Size", selection: $size) { + Text("Small").tag(0) + Text("Medium").tag(1) + Text("Large").tag(2) + } + Text("Size index: \(size)") + } + } + } + } + } +} + +struct ProgressViewPlayground: View { + @State private var progress = 0.25 + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Example("Indeterminate") { ProgressView() } + Example("Determinate") { + VStack(alignment: .leading, spacing: 8) { + ProgressView(value: progress) + Text("\(Int(progress * 100))%") + Button("Advance") { progress = progress >= 1 ? 0 : progress + 0.25 } + } + } + } + } + } +} diff --git a/Demo/App.swiftpm/Sources/LayoutPlaygrounds.swift b/Demo/App.swiftpm/Sources/LayoutPlaygrounds.swift new file mode 100644 index 0000000..ba26dd8 --- /dev/null +++ b/Demo/App.swiftpm/Sources/LayoutPlaygrounds.swift @@ -0,0 +1,90 @@ +#if canImport(AndroidSwiftUI) +import AndroidSwiftUI +#else +import SwiftUI +#endif + +struct StackPlayground: View { + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Example("VStack .leading") { + VStack(alignment: .leading) { Text("Row one"); Text("Row two, longer") } + } + Example("VStack .trailing") { + VStack(alignment: .trailing) { Text("Row one"); Text("Row two, longer") } + } + Example("HStack with Spacer") { + HStack { Text("Start"); Spacer(); Text("End") } + } + Example("ZStack .center") { + ZStack { + Color.blue.frame(width: 220, height: 100) + Text("Overlaid") + } + } + Example("ZStack .bottomTrailing") { + ZStack(alignment: .bottomTrailing) { + Color.green.frame(width: 220, height: 100) + Text("Corner") + } + } + } + } + } +} + +struct SpacerDividerPlayground: View { + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Example("Spacer pushes apart") { + HStack { Text("Left"); Spacer(); Text("Right") } + } + Example("Divider between rows") { + VStack(alignment: .leading, spacing: 8) { + Text("Above") + Divider() + Text("Below") + } + } + } + } + } +} + +struct ColorPlayground: View { + private let swatches: [(String, Color)] = [ + ("blue", .blue), ("green", .green), ("orange", .orange), + ("purple", .purple), ("pink", .pink), ("red", .red), + ("yellow", .yellow), ("gray", .gray), + ] + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + ForEach(swatches, id: \.0) { swatch in + Example(swatch.0) { + swatch.1.frame(height: 44).cornerRadius(8) + } + } + } + } + } +} + +struct ScrollViewPlayground: View { + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 8) { + Text("Scroll to see all rows") + .padding() + ForEach(1...40, id: \.self) { n in + Text("Scrollable row \(n)") + .padding() + .background(n % 2 == 0 ? Color.blue : Color.orange) + .cornerRadius(6) + } + } + } + } +} diff --git a/Demo/App.swiftpm/Sources/PresentationPlaygrounds.swift b/Demo/App.swiftpm/Sources/PresentationPlaygrounds.swift new file mode 100644 index 0000000..5fe8b88 --- /dev/null +++ b/Demo/App.swiftpm/Sources/PresentationPlaygrounds.swift @@ -0,0 +1,108 @@ +#if canImport(AndroidSwiftUI) +import AndroidSwiftUI +#else +import SwiftUI +#endif + +struct NavigationPlayground: View { + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 16) { + Text("Value-based navigation").padding() + NavigationLink("Push value 1", value: 1) + NavigationLink("Push value 2", value: 2) + Divider() + Text("Classic navigation").padding() + NavigationLink("Push a destination view", destination: NavDetail(label: "Pushed directly")) + } + } + .navigationDestination(for: Int.self) { value in + NavDetail(label: "Value \(value)", next: value + 1) + } + } +} + +struct NavDetail: View { + let label: String + var next: Int? = nil + @Environment(\.dismiss) private var dismiss + var body: some View { + VStack(spacing: 16) { + Text(label) + if let next, next <= 4 { + NavigationLink("Push value \(next)", value: next) + } + Button("Pop with dismiss") { dismiss() } + } + .padding() + .navigationTitle(label) + } +} + +struct TabViewPlayground: View { + @State private var selection = 0 + var body: some View { + TabView(selection: $selection) { + VStack(spacing: 16) { + Text("First tab content") + Button("Jump to third tab") { selection = 2 } + } + .tabItem { Text("One") }.tag(0) + Text("Second tab content").tabItem { Text("Two") }.tag(1) + Text("Third tab content").tabItem { Text("Three") }.tag(2) + } + } +} + +struct SheetPlayground: View { + @State private var full = false + @State private var medium = false + var body: some View { + ScrollView { + VStack(spacing: 16) { + Button("Present full-size sheet") { full = true } + Button("Present medium detent sheet") { medium = true } + } + .padding() + } + .sheet(isPresented: $full) { + SheetBody(title: "Full-size sheet") + } + .sheet(isPresented: $medium) { + SheetBody(title: "Medium sheet").presentationDetents([.medium]) + } + } +} + +struct SheetBody: View { + let title: String + @Environment(\.dismiss) private var dismiss + var body: some View { + VStack(spacing: 16) { + Text(title) + Button("Dismiss") { dismiss() } + } + .padding() + } +} + +struct AlertPlayground: View { + @State private var simple = false + @State private var confirm = false + @State private var result = "No choice yet" + var body: some View { + ScrollView { + VStack(spacing: 16) { + Button("Show simple alert") { simple = true } + Button("Show confirmation") { confirm = true } + Text(result) + } + .padding() + } + .alert("A simple alert", isPresented: $simple, message: "This is the message body.") + .alert("Delete item?", isPresented: $confirm, message: "This cannot be undone.", buttons: [ + AlertButton("Cancel", role: .cancel) { result = "Cancelled" }, + AlertButton("Delete", role: .destructive) { result = "Deleted" }, + ]) + } +} diff --git a/Demo/App.swiftpm/Sources/StatePlaygrounds.swift b/Demo/App.swiftpm/Sources/StatePlaygrounds.swift new file mode 100644 index 0000000..af9aac2 --- /dev/null +++ b/Demo/App.swiftpm/Sources/StatePlaygrounds.swift @@ -0,0 +1,82 @@ +#if canImport(AndroidSwiftUI) +import AndroidSwiftUI +#else +import SwiftUI +#endif + +import Observation + +struct StatePlayground: View { + @State private var parentRenders = 0 + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Example("Parent re-renders: \(parentRenders)") { + Button("Re-render parent") { parentRenders += 1 } + } + Example("Child keeps its own state") { + StateChild() + } + } + } + } +} + +struct StateChild: View { + @State private var localCount = 0 + var body: some View { + VStack(alignment: .leading, spacing: 8) { + Text("Local count: \(localCount)") + Button("Bump child") { localCount += 1 } + Text("Survives parent re-renders") + } + } +} + +@Observable +final class CounterModel { + var value = 0 +} + +struct EnvironmentPlayground: View { + @State private var model = CounterModel() + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Example("Injected object") { + EnvReader().environment(model) + } + Example("Read here too") { + EnvReader().environment(model) + } + } + } + } +} + +struct EnvReader: View { + @Environment(CounterModel.self) private var model + var body: some View { + VStack(alignment: .leading, spacing: 8) { + Text("Shared value: \(model.value)") + Button("Increment shared") { model.value += 1 } + } + } +} + +struct ObservablePlayground: View { + @State private var model = CounterModel() + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Example("Observation drives updates") { + VStack(alignment: .leading, spacing: 8) { + Text("Observable value: \(model.value)") + Button("Increment") { model.value += 1 } + Text("Mutating the model re-evaluates automatically") + } + } + } + } + } +} diff --git a/Sources/SwiftUIDesktopDemo/DesktopDemo.swift b/Sources/SwiftUIDesktopDemo/DesktopDemo.swift index e43df5f..fe71743 100644 --- a/Sources/SwiftUIDesktopDemo/DesktopDemo.swift +++ b/Sources/SwiftUIDesktopDemo/DesktopDemo.swift @@ -6,23 +6,11 @@ // pushed through the bridge into the rig's Compose window. // +import Foundation import AndroidSwiftUICore import AndroidSwiftUIBridge import SwiftJava -struct CounterDemo: View { - - @State private var count = 0 - - var body: some View { - VStack(spacing: 12) { - Text("Count: \(count)") - Button("Increment") { count += 1 } - Toggle("Feature flag", isOn: .constant(true)) - } - } -} - @JavaClass("com.pureswift.swiftui.desktop.SwiftRuntime") open class SwiftRuntime: JavaObject { } @@ -33,7 +21,11 @@ extension SwiftRuntime { @JavaMethod func start(_ store: TreeStore?) { guard let store else { return } - let runtime = BridgeRuntime(root: CounterDemo(), store: store) + // marshal re-renders onto the main thread; async state (e.g. a List + // refresh Task) writes off-thread and JNI object creation must run here + let runtime = BridgeRuntime(root: ContentView(), store: store) { block in + DispatchQueue.main.async { block() } + } runtime.start() } } diff --git a/Demo/App.swiftpm/Sources/ButtonScreen.swift b/Sources/SwiftUIDesktopDemo/Gallery/ButtonScreen.swift similarity index 88% rename from Demo/App.swiftpm/Sources/ButtonScreen.swift rename to Sources/SwiftUIDesktopDemo/Gallery/ButtonScreen.swift index dfddee3..42d9837 100644 --- a/Demo/App.swiftpm/Sources/ButtonScreen.swift +++ b/Sources/SwiftUIDesktopDemo/Gallery/ButtonScreen.swift @@ -1,8 +1,4 @@ -#if canImport(AndroidSwiftUI) -import AndroidSwiftUI -#else -import SwiftUI -#endif +import AndroidSwiftUICore /// Button initializers and actions. struct ButtonScreen: View { diff --git a/Demo/App.swiftpm/Sources/ContentView.swift b/Sources/SwiftUIDesktopDemo/Gallery/ContentView.swift similarity index 94% rename from Demo/App.swiftpm/Sources/ContentView.swift rename to Sources/SwiftUIDesktopDemo/Gallery/ContentView.swift index c3f81bf..c61fbdc 100644 --- a/Demo/App.swiftpm/Sources/ContentView.swift +++ b/Sources/SwiftUIDesktopDemo/Gallery/ContentView.swift @@ -1,8 +1,4 @@ -#if canImport(AndroidSwiftUI) -import AndroidSwiftUI -#else -import SwiftUI -#endif +import AndroidSwiftUICore /// Gallery of playground screens, navigated with a `NavigationStack`. Each row /// pushes a feature screen; `List`-based navigation returns with lazy diff --git a/Demo/App.swiftpm/Sources/ControlsScreen.swift b/Sources/SwiftUIDesktopDemo/Gallery/ControlsScreen.swift similarity index 95% rename from Demo/App.swiftpm/Sources/ControlsScreen.swift rename to Sources/SwiftUIDesktopDemo/Gallery/ControlsScreen.swift index 82ab822..9a93bec 100644 --- a/Demo/App.swiftpm/Sources/ControlsScreen.swift +++ b/Sources/SwiftUIDesktopDemo/Gallery/ControlsScreen.swift @@ -1,8 +1,4 @@ -#if canImport(AndroidSwiftUI) -import AndroidSwiftUI -#else -import SwiftUI -#endif +import AndroidSwiftUICore /// Form controls and their state bindings. struct ControlsScreen: View { diff --git a/Demo/App.swiftpm/Sources/GridScreen.swift b/Sources/SwiftUIDesktopDemo/Gallery/GridScreen.swift similarity index 93% rename from Demo/App.swiftpm/Sources/GridScreen.swift rename to Sources/SwiftUIDesktopDemo/Gallery/GridScreen.swift index 9dd00cc..c941409 100644 --- a/Demo/App.swiftpm/Sources/GridScreen.swift +++ b/Sources/SwiftUIDesktopDemo/Gallery/GridScreen.swift @@ -1,8 +1,4 @@ -#if canImport(AndroidSwiftUI) -import AndroidSwiftUI -#else -import SwiftUI -#endif +import AndroidSwiftUICore /// Lazy grids. struct GridScreen: View { diff --git a/Demo/App.swiftpm/Sources/ListScreen.swift b/Sources/SwiftUIDesktopDemo/Gallery/ListScreen.swift similarity index 89% rename from Demo/App.swiftpm/Sources/ListScreen.swift rename to Sources/SwiftUIDesktopDemo/Gallery/ListScreen.swift index b46c9d9..d16854c 100644 --- a/Demo/App.swiftpm/Sources/ListScreen.swift +++ b/Sources/SwiftUIDesktopDemo/Gallery/ListScreen.swift @@ -1,8 +1,4 @@ -#if canImport(AndroidSwiftUI) -import AndroidSwiftUI -#else -import SwiftUI -#endif +import AndroidSwiftUICore /// A lazy list with pull to refresh. Rows are evaluated on demand, so the /// large-list variant scrolls without materializing every row up front. diff --git a/Demo/App.swiftpm/Sources/ModifierScreen.swift b/Sources/SwiftUIDesktopDemo/Gallery/ModifierScreen.swift similarity index 96% rename from Demo/App.swiftpm/Sources/ModifierScreen.swift rename to Sources/SwiftUIDesktopDemo/Gallery/ModifierScreen.swift index 57bb779..8733e0d 100644 --- a/Demo/App.swiftpm/Sources/ModifierScreen.swift +++ b/Sources/SwiftUIDesktopDemo/Gallery/ModifierScreen.swift @@ -1,8 +1,4 @@ -#if canImport(AndroidSwiftUI) -import AndroidSwiftUI -#else -import SwiftUI -#endif +import AndroidSwiftUICore /// Layout and effect modifiers. struct ModifierScreen: View { diff --git a/Demo/App.swiftpm/Sources/NavigationScreen.swift b/Sources/SwiftUIDesktopDemo/Gallery/NavigationScreen.swift similarity index 95% rename from Demo/App.swiftpm/Sources/NavigationScreen.swift rename to Sources/SwiftUIDesktopDemo/Gallery/NavigationScreen.swift index 251f973..a9f62bb 100644 --- a/Demo/App.swiftpm/Sources/NavigationScreen.swift +++ b/Sources/SwiftUIDesktopDemo/Gallery/NavigationScreen.swift @@ -1,8 +1,4 @@ -#if canImport(AndroidSwiftUI) -import AndroidSwiftUI -#else -import SwiftUI -#endif +import AndroidSwiftUICore /// Classic and value-based navigation, plus the dismiss environment action. struct NavigationScreen: View { diff --git a/Demo/App.swiftpm/Sources/ObservationScreen.swift b/Sources/SwiftUIDesktopDemo/Gallery/ObservationScreen.swift similarity index 89% rename from Demo/App.swiftpm/Sources/ObservationScreen.swift rename to Sources/SwiftUIDesktopDemo/Gallery/ObservationScreen.swift index 3678f1f..4c3c01b 100644 --- a/Demo/App.swiftpm/Sources/ObservationScreen.swift +++ b/Sources/SwiftUIDesktopDemo/Gallery/ObservationScreen.swift @@ -1,8 +1,4 @@ -#if canImport(AndroidSwiftUI) -import AndroidSwiftUI -#else -import SwiftUI -#endif +import AndroidSwiftUICore import Observation diff --git a/Demo/App.swiftpm/Sources/SheetScreen.swift b/Sources/SwiftUIDesktopDemo/Gallery/SheetScreen.swift similarity index 92% rename from Demo/App.swiftpm/Sources/SheetScreen.swift rename to Sources/SwiftUIDesktopDemo/Gallery/SheetScreen.swift index 2db7c54..e738d33 100644 --- a/Demo/App.swiftpm/Sources/SheetScreen.swift +++ b/Sources/SwiftUIDesktopDemo/Gallery/SheetScreen.swift @@ -1,8 +1,4 @@ -#if canImport(AndroidSwiftUI) -import AndroidSwiftUI -#else -import SwiftUI -#endif +import AndroidSwiftUICore /// Sheet presentation, detents and the dismiss environment action. struct SheetScreen: View { diff --git a/Demo/App.swiftpm/Sources/StacksScreen.swift b/Sources/SwiftUIDesktopDemo/Gallery/StacksScreen.swift similarity index 95% rename from Demo/App.swiftpm/Sources/StacksScreen.swift rename to Sources/SwiftUIDesktopDemo/Gallery/StacksScreen.swift index 02ec94c..e31a63c 100644 --- a/Demo/App.swiftpm/Sources/StacksScreen.swift +++ b/Sources/SwiftUIDesktopDemo/Gallery/StacksScreen.swift @@ -1,8 +1,4 @@ -#if canImport(AndroidSwiftUI) -import AndroidSwiftUI -#else -import SwiftUI -#endif +import AndroidSwiftUICore /// Stack containers and cross-axis alignment. struct StacksScreen: View { diff --git a/Demo/App.swiftpm/Sources/StateScreen.swift b/Sources/SwiftUIDesktopDemo/Gallery/StateScreen.swift similarity index 92% rename from Demo/App.swiftpm/Sources/StateScreen.swift rename to Sources/SwiftUIDesktopDemo/Gallery/StateScreen.swift index 35f1eb5..d742f52 100644 --- a/Demo/App.swiftpm/Sources/StateScreen.swift +++ b/Sources/SwiftUIDesktopDemo/Gallery/StateScreen.swift @@ -1,8 +1,4 @@ -#if canImport(AndroidSwiftUI) -import AndroidSwiftUI -#else -import SwiftUI -#endif +import AndroidSwiftUICore /// Lazy state initialization: the stored class is constructed once per view /// lifetime, no matter how often the parent re-renders. diff --git a/Demo/App.swiftpm/Sources/TabScreen.swift b/Sources/SwiftUIDesktopDemo/Gallery/TabScreen.swift similarity index 88% rename from Demo/App.swiftpm/Sources/TabScreen.swift rename to Sources/SwiftUIDesktopDemo/Gallery/TabScreen.swift index 80ca2e3..cb4199d 100644 --- a/Demo/App.swiftpm/Sources/TabScreen.swift +++ b/Sources/SwiftUIDesktopDemo/Gallery/TabScreen.swift @@ -1,8 +1,4 @@ -#if canImport(AndroidSwiftUI) -import AndroidSwiftUI -#else -import SwiftUI -#endif +import AndroidSwiftUICore /// Tab bar with selection. struct TabScreen: View { diff --git a/Demo/App.swiftpm/Sources/TextScreen.swift b/Sources/SwiftUIDesktopDemo/Gallery/TextScreen.swift similarity index 86% rename from Demo/App.swiftpm/Sources/TextScreen.swift rename to Sources/SwiftUIDesktopDemo/Gallery/TextScreen.swift index 99d016d..4285941 100644 --- a/Demo/App.swiftpm/Sources/TextScreen.swift +++ b/Sources/SwiftUIDesktopDemo/Gallery/TextScreen.swift @@ -1,8 +1,4 @@ -#if canImport(AndroidSwiftUI) -import AndroidSwiftUI -#else -import SwiftUI -#endif +import AndroidSwiftUICore /// Text initializers and dynamic content. struct TextScreen: View {