| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573 |
- import Foundation
- extension BackendFeatures {
- /// A protocol that provides default implementations of all methods in
- /// ``BaseAppBackend`` so as to enable rapid iteration on custom backends.
- ///
- /// - Warning: **This protocol is _not_ intended to be used by
- /// production-ready backends!** Every default implementation that it
- /// provides will crash the user's app when called. This protocol is
- /// solely intended to make the compiler shut up when you're in the middle
- /// of implementing a brand-new backend.
- ///
- /// If you are unable to write a required backend method because your
- /// underlying UI framework doesn't properly support the associated
- /// functionality, you can call `fatalError` in your own implementation
- /// of the method. That way you'll still get notified by the compiler if
- /// we add new methods in future SwiftCrossUI versions.
- ///
- /// ## Workflow
- ///
- /// A typical workflow for implementing a new backend might go something
- /// like this:
- ///
- /// 1. Declare a type (usually a `class`, but there's no technical reason
- /// it can't be a `struct`) that conforms to `BaseStubs`.
- /// 2. Write a small example app using that type as its backend.
- /// 3. Implement enough of ``Core`` to get the example app to launch and
- /// show an empty window.
- /// 4. Switch the conformance over to ``BaseAppBackend``, and examine the
- /// compiler errors for suitable methods to implement.
- /// 5. Copy their declarations into the type, then switch back over to
- /// `BaseStubs` so the backend compiles again.
- /// 6. Iterate on the method implementations until they work properly.
- /// 7. Repeat steps 4 through 6 until you have a more-or-less complete
- /// backend.
- ///
- /// Of course, you can use whatever workflow works best for you; this
- /// protocol just serves as a tool to keep you from having to implement the
- /// entire backend in one go.
- #if !DEBUG
- @available(
- *,
- deprecated,
- message: """
- 'BaseStubs' should not be used in release builds, conform to 'BaseAppBackend' instead
- """
- )
- #endif
- public protocol BaseStubs: BaseAppBackend {}
- }
- // This type isn't actually used anywhere, so keep it out of release builds.
- #if DEBUG
- /// A backend "implementation" solely for testing whether
- /// ``BackendFeatures/BaseStubs`` has default implementations for all required
- /// backend features.
- ///
- /// Aside from empty nested structs to satisfy associated type requirements,
- /// **this struct must remain empty** -- all backend methods and properties
- /// should have default implementations provided by `BaseStubs`. If any errors
- /// show up here, and you've added empty structs for all associated types,
- /// you're missing some default implementations.
- ///
- /// 1. Accept all fix-mes for the errors in question.
- /// 2. Move the compiler-generated declarations out of this type and into the
- /// `BaseStubs` extension just below this type in the
- /// `BackendFeatures+BaseStubs` file. (Precisely _where_ you move them is
- /// unimportant, just try to keep some semblance of a logical order.)
- /// 3. **Make all declarations `public`.** This is important, and the
- /// compiler likely won't help you here because this struct is `private`.
- /// 4. Write `todo()` in the bodies of every method and property.
- private struct BaseStubsTest: BackendFeatures.BaseStubs {
- struct Window {}
- struct Widget {}
- }
- #endif
- #if !DEBUG
- @available(*, deprecated)
- #endif
- extension BackendFeatures.BaseStubs {
- fileprivate func todo(function: String = #function) -> Never {
- print("FATAL_TODO: \(Self.self): \(function) not implemented")
- fflush(nil)
- fatalError("\(Self.self): \(function) not implemented")
- }
- }
- #if !DEBUG
- @available(*, deprecated)
- #endif
- extension BackendFeatures.BaseStubs {
- public func createScrollContainer(for child: Widget) -> Widget {
- todo()
- }
- public func updateScrollContainer(
- _ scrollView: Widget,
- environment: EnvironmentValues,
- bounceHorizontally: Bool,
- bounceVertically: Bool,
- hasHorizontalScrollBar: Bool,
- hasVerticalScrollBar: Bool
- ) {
- todo()
- }
- public func createSelectableListView() -> Widget {
- todo()
- }
- public func updateSelectableListView(
- _ selectableListView: Widget,
- environment: EnvironmentValues
- ) {
- todo()
- }
- public func baseItemPadding(ofSelectableListView listView: Widget) -> EdgeInsets {
- todo()
- }
- public func minimumRowSize(ofSelectableListView listView: Widget) -> SIMD2<Int> {
- todo()
- }
- public func setItems(
- ofSelectableListView listView: Widget,
- to items: [Widget],
- withRowHeights rowHeights: [Int]
- ) {
- todo()
- }
- public func setSelectionHandler(
- forSelectableListView listView: Widget,
- to action: @escaping (Int) -> Void
- ) {
- todo()
- }
- public func setSelectedItem(
- ofSelectableListView listView: Widget,
- toItemAt index: Int?
- ) {
- todo()
- }
- public func createSplitView(leadingChild: Widget, trailingChild: Widget) -> Widget {
- todo()
- }
- public func setResizeHandler(
- ofSplitView splitView: Widget,
- to action: @escaping () -> Void
- ) {
- todo()
- }
- public func sidebarWidth(ofSplitView splitView: Widget) -> Int {
- todo()
- }
- public func setSidebarWidthBounds(
- ofSplitView splitView: Widget,
- minimum minimumWidth: Int,
- maximum maximumWidth: Int
- ) {
- todo()
- }
- public func size(
- of text: String,
- whenDisplayedIn widget: Widget,
- proposedWidth: Int?,
- proposedHeight: Int?,
- environment: EnvironmentValues
- ) -> SIMD2<Int> {
- todo()
- }
- public func createTextView() -> Widget {
- todo()
- }
- public func updateTextView(
- _ textView: Widget,
- content: String,
- environment: EnvironmentValues
- ) {
- todo()
- }
- public func createImageView() -> Widget {
- todo()
- }
- public func updateImageView(
- _ imageView: Widget,
- rgbaData: [UInt8],
- width: Int,
- height: Int,
- targetWidth: Int,
- targetHeight: Int,
- dataHasChanged: Bool,
- environment: EnvironmentValues
- ) {
- todo()
- }
- public func createSimpleButton() -> Widget {
- todo()
- }
- public func updateSimpleButton(
- _ button: Widget,
- label: String,
- environment: EnvironmentValues,
- action: @escaping () -> Void
- ) {
- todo()
- }
- public func createButton(
- wrapping: Widget
- ) -> Widget {
- todo()
- }
- public func updateButton(
- _ button: Widget,
- environment: EnvironmentValues,
- action: @escaping () -> Void
- ) {
- todo()
- }
- public func buttonPadding(in environment: EnvironmentValues) -> SIMD2<Int> {
- todo()
- }
- public func defaultButtonStyle() -> ButtonStyle {
- todo()
- }
- public func createToggle() -> Widget {
- todo()
- }
- public func updateToggle(
- _ toggle: Widget,
- label: String,
- environment: EnvironmentValues,
- onChange: @escaping (Bool) -> Void
- ) {
- todo()
- }
- public func setState(ofToggle toggle: Widget, to state: Bool) {
- todo()
- }
- public func createSwitch() -> Widget {
- todo()
- }
- public func updateSwitch(
- _ switchWidget: Widget,
- environment: EnvironmentValues,
- onChange: @escaping (Bool) -> Void
- ) {
- todo()
- }
- public func setState(ofSwitch switchWidget: Widget, to state: Bool) {
- todo()
- }
- public func createCheckbox() -> Widget {
- todo()
- }
- public func updateCheckbox(
- _ checkboxWidget: Widget,
- environment: EnvironmentValues,
- onChange: @escaping (Bool) -> Void
- ) {
- todo()
- }
- public func setState(ofCheckbox checkboxWidget: Widget, to state: Bool) {
- todo()
- }
- public func createSlider() -> Widget {
- todo()
- }
- public func updateSlider(
- _ slider: Widget,
- minimum: Double,
- maximum: Double,
- decimalPlaces: Int,
- environment: EnvironmentValues,
- onChange: @escaping (Double) -> Void
- ) {
- todo()
- }
- public func setValue(ofSlider slider: Widget, to value: Double) {
- todo()
- }
- public func createTextField() -> Widget {
- todo()
- }
- public func updateTextField(
- _ textField: Widget,
- placeholder: String,
- environment: EnvironmentValues,
- onChange: @escaping (String) -> Void,
- onSubmit: @escaping () -> Void
- ) {
- todo()
- }
- public func setContent(ofTextField textField: Widget, to content: String) {
- todo()
- }
- public func getContent(ofTextField textField: Widget) -> String {
- todo()
- }
- public func setContent(ofSecureField secureField: Widget, to content: String) {
- todo()
- }
- public func getContent(ofSecureField secureField: Widget) -> String {
- todo()
- }
- public func createTextEditor() -> Widget {
- todo()
- }
- public func updateTextEditor(
- _ textEditor: Widget,
- environment: EnvironmentValues,
- onChange: @escaping (String) -> Void
- ) {
- todo()
- }
- public func setContent(ofTextEditor textEditor: Widget, to content: String) {
- todo()
- }
- public func getContent(ofTextEditor textEditor: Widget) -> String {
- todo()
- }
- public func createPicker(style: BackendPickerStyle) -> Widget {
- todo()
- }
- public func updatePicker(
- _ picker: Widget,
- options: [String],
- environment: EnvironmentValues,
- onChange: @escaping (Int?) -> Void
- ) {
- todo()
- }
- public func setSelectedOption(ofPicker picker: Widget, to selectedOption: Int?) {
- todo()
- }
- public func createProgressSpinner() -> Widget {
- todo()
- }
- public func createProgressBar() -> Widget {
- todo()
- }
- public func updateProgressBar(
- _ widget: Widget,
- progressFraction: Double?,
- environment: EnvironmentValues
- ) {
- todo()
- }
- public var deviceClass: DeviceClass {
- todo()
- }
- public func runMainLoop(_ callback: @escaping @MainActor () -> Void) {
- todo()
- }
- public func runInMainThread(action: @escaping @MainActor () -> Void) {
- todo()
- }
- public func computeRootEnvironment(defaultEnvironment: EnvironmentValues) -> EnvironmentValues {
- todo()
- }
- public func setRootEnvironmentChangeHandler(
- to action: @escaping @Sendable @MainActor () -> Void
- ) {
- todo()
- }
- public var defaultPaddingAmount: Int {
- todo()
- }
- public func show(widget: Widget) {
- todo()
- }
- public func naturalSize(of widget: Widget) -> SIMD2<Int> {
- todo()
- }
- public func setSize(of widget: Widget, to size: SIMD2<Int>) {
- todo()
- }
- public var supportsMultipleWindows: Bool {
- todo()
- }
- public var canOverrideWindowColorScheme: Bool {
- todo()
- }
- public var restoresWindowFrames: Bool {
- todo()
- }
- public func createWindow(withDefaultSize defaultSize: SIMD2<Int>?, id: String) -> Window {
- todo()
- }
- public func updateWindow(_ window: Window, environment: EnvironmentValues) {
- todo()
- }
- public func setTitle(ofWindow window: Window, to title: String) {
- todo()
- }
- public func setChild(ofWindow window: Window, to child: Widget) {
- todo()
- }
- public func size(ofWindow window: Window) -> SIMD2<Int> {
- todo()
- }
- public func isWindowProgrammaticallyResizable(_ window: Window) -> Bool {
- todo()
- }
- public func setSize(ofWindow window: Window, to newSize: SIMD2<Int>) {
- todo()
- }
- public func setSizeLimits(
- ofWindow window: Window,
- minimum minimumSize: SIMD2<Int>,
- maximum maximumSize: SIMD2<Int>?
- ) {
- todo()
- }
- public func setResizeHandler(
- ofWindow window: Window,
- to action: @escaping (SIMD2<Int>) -> Void
- ) {
- todo()
- }
- public func show(window: Window) {
- todo()
- }
- public func activate(window: Window) {
- todo()
- }
- public func computeWindowEnvironment(
- window: Window,
- rootEnvironment: EnvironmentValues
- ) -> EnvironmentValues {
- todo()
- }
- public func setWindowEnvironmentChangeHandler(
- of window: Window,
- to action: @escaping @Sendable @MainActor () -> Void
- ) {
- todo()
- }
- public func createContainer() -> Widget {
- todo()
- }
- public func removeAllChildren(of container: Widget) {
- todo()
- }
- public func insert(_ child: Widget, into container: Widget, at index: Int) {
- todo()
- }
- public func swap(
- childAt firstIndex: Int,
- withChildAt secondIndex: Int,
- in container: Widget
- ) {
- todo()
- }
- public func setPosition(
- ofChildAt index: Int,
- in container: Widget,
- to position: SIMD2<Int>
- ) {
- todo()
- }
- public func remove(childAt index: Int, from container: Widget) {
- todo()
- }
- public var scrollBarWidth: Int {
- todo()
- }
- public var requiresImageUpdateOnScaleFactorChange: Bool {
- todo()
- }
- public var requiresToggleSwitchSpacer: Bool {
- todo()
- }
- public func createSecureField() -> Widget {
- todo()
- }
- public func updateSecureField(
- _ secureField: Widget,
- placeholder: String,
- environment: EnvironmentValues,
- onChange: @escaping (String) -> Void,
- onSubmit: @escaping () -> Void
- ) {
- todo()
- }
- public var supportedPickerStyles: [BackendPickerStyle] {
- todo()
- }
- }
|