diff --git a/CHANGELOG.md b/CHANGELOG.md index 12010e28732..94f54430dac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ This is just the changelog for the core `egui` crate. Every crate in this reposi * [`epaint` changelog](crates/epaint/CHANGELOG.md) * [`egui-winit` changelog](crates/egui-winit/CHANGELOG.md) * [`egui-wgpu` changelog](crates/egui-wgpu/CHANGELOG.md) +* [`egui-ios` changelog](crates/egui-ios/CHANGELOG.md) * [`egui_kittest` changelog](crates/egui_kittest/CHANGELOG.md) * [`egui_glow` changelog](crates/egui_glow/CHANGELOG.md) * [`ecolor` changelog](crates/ecolor/CHANGELOG.md) diff --git a/Cargo.toml b/Cargo.toml index b291cb36c7d..639f4b0f52e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "crates/egui_demo_lib", "crates/egui_extras", "crates/egui_glow", + "crates/egui-ios", "crates/egui_kittest", "crates/egui-wgpu", "crates/egui-winit", @@ -65,6 +66,7 @@ egui_extras = { version = "0.33.3", path = "crates/egui_extras", default-feature egui-wgpu = { version = "0.33.3", path = "crates/egui-wgpu", default-features = false } egui_demo_lib = { version = "0.33.3", path = "crates/egui_demo_lib", default-features = false } egui_glow = { version = "0.33.3", path = "crates/egui_glow", default-features = false } +egui-ios = { version = "0.1.0", path = "crates/egui-ios", default-features = false } egui_kittest = { version = "0.33.3", path = "crates/egui_kittest", default-features = false } eframe = { version = "0.33.3", path = "crates/eframe", default-features = false } diff --git a/README.md b/README.md index f4a09446563..9a4cbdcea0f 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ egui aims to be the easiest-to-use Rust GUI library, and the simplest way to mak egui can be used anywhere you can draw textured triangles, which means you can easily integrate it into your game engine of choice. -[`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe) is the official egui framework, which supports writing apps for Web, Linux, Mac, Windows, and Android. +[`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe) is the official egui framework, which supports writing apps for Web, Linux, Mac, Windows, Android, and iOS. ## Example diff --git a/crates/egui-ios/CHANGELOG.md b/crates/egui-ios/CHANGELOG.md new file mode 100644 index 00000000000..f100dbb5b11 --- /dev/null +++ b/crates/egui-ios/CHANGELOG.md @@ -0,0 +1,14 @@ +# Changelog for egui-ios +All notable changes to the `egui-ios` crate will be noted in this file. + +This file is updated upon each release. +Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. + + +## 0.33.3 - 2025-12-11 +### ⭐ Added +* Initial release of `egui-ios` crate +* `InputEvent` - FFI type for touch, keyboard, and lifecycle events from Swift to egui +* `OutputState` - FFI type for cursor, keyboard, and IME state from egui to Swift +* `CursorIcon` - Cursor icons mapped to iOS equivalents +* Swift bindings generated via swift-bridge diff --git a/crates/egui-ios/Cargo.toml b/crates/egui-ios/Cargo.toml new file mode 100644 index 00000000000..777688c8995 --- /dev/null +++ b/crates/egui-ios/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "egui-ios" +version = "0.1.0" +edition.workspace = true +license.workspace = true +rust-version.workspace = true +description = "iOS FFI bindings for egui via swift-bridge" +repository = "https://github.com/emilk/egui" +keywords = ["egui", "ios", "swift", "ffi"] +categories = ["gui", "rendering"] +readme = "README.md" + +[dependencies] +egui.workspace = true +swift-bridge = "0.1" + +[build-dependencies] +swift-bridge-build = "0.1" + +# Override workspace lints - FFI code requires unsafe +[lints.rust] +unsafe_code = "allow" + +[features] +default = [] diff --git a/crates/egui-ios/README.md b/crates/egui-ios/README.md new file mode 100644 index 00000000000..b2f81826d87 --- /dev/null +++ b/crates/egui-ios/README.md @@ -0,0 +1,98 @@ +# egui-ios + +iOS FFI bindings for [egui](https://github.com/emilk/egui) via [swift-bridge](https://github.com/chinedufn/swift-bridge). + +**Minimum iOS version: 13.0** + +This crate provides Swift-compatible types for embedding egui in iOS apps: + +- `InputEvent` - Touch, keyboard, and lifecycle events from Swift to egui +- `OutputState` - Cursor, keyboard, and IME state from egui to Swift +- `CursorIcon` - Cursor icons mapped to iOS equivalents + +## Usage + +Add to your `Cargo.toml`: + +```toml +[dependencies] +egui-ios = "0.1" +``` + +In your Rust code: + +```rust +use egui_ios::{InputEvent, OutputState, CursorIcon}; + +// Convert input events to egui events +let egui_events: Vec = input_events + .into_iter() + .filter_map(|e| e.into_egui_event()) + .collect(); + +// Run egui frame +let full_output = ctx.run(raw_input, |ctx| { + // Your UI code +}); + +// Create output state for Swift +let output = OutputState::with_keyboard_state( + full_output.platform_output.cursor_icon.into(), + ctx.wants_keyboard_input(), + full_output.platform_output.ime.as_ref().map(|ime| ime.rect), +); +``` + +## Swift Integration + +Pre-generated Swift bindings are included in `generated/`. Add these files to your Xcode project: + +- `generated/SwiftBridgeCore.swift` - swift-bridge runtime +- `generated/SwiftBridgeCore.h` - C header for runtime +- `generated/egui-ios/egui-ios.swift` - Generated Swift bindings +- `generated/egui-ios/egui-ios.h` - C header for bindings + +The generated files are committed to the repository so Swift developers don't need to run the Rust build step. + +### Integration Overview + +A typical SwiftUI integration involves: +1. Creating a `MTKView` for Metal rendering +2. Forwarding touch events via `InputEvent::from_*` methods +3. Running egui each frame and checking `OutputState` for keyboard/IME state +4. Using a native `UITextField` overlay for text input with autocomplete/autocorrect + +## Input Events + +| Event | Description | +|-------|-------------| +| `from_pointer_moved(x, y)` | Touch/pointer position | +| `from_left_mouse_down(x, y, pressed)` | Primary touch | +| `from_mouse_wheel(x, y)` | Scroll gesture | +| `from_text_commit(text)` | Committed text after autocomplete | +| `from_ime_preedit(text)` | IME composition text | +| `from_virtual_key(code, pressed)` | Special keys (backspace, enter, etc.) | +| `from_scene_phase_changed(phase)` | iOS scene lifecycle | + +## Virtual Key Codes + +| Code | Key | +|------|-----| +| 0 | Backspace | +| 1 | Enter | +| 2 | Tab | +| 3 | Escape | +| 4-7 | Arrow keys (up, down, left, right) | + +## Output State + +Check `OutputState` after each frame: + +- `wants_keyboard()` - Show/hide iOS keyboard +- `has_ime_rect()` / `get_ime_rect_*()` - Keyboard positioning +- `get_cursor_icon()` - Cursor for pointer interactions + +## Known Limitations + +- **Clipboard**: The `arboard` crate doesn't support iOS yet, so `egui-winit` disables system clipboard on iOS. Apps should implement clipboard via `UIPasteboard` in Swift and relay it through input events. +- **Safe area**: Uses a custom objc2 bridge in `egui-winit` until winit 0.31+ adds native `Window::safe_area()` support. diff --git a/crates/egui-ios/build.rs b/crates/egui-ios/build.rs new file mode 100644 index 00000000000..a39111af1da --- /dev/null +++ b/crates/egui-ios/build.rs @@ -0,0 +1,14 @@ +use std::path::PathBuf; + +fn main() { + let out_dir = PathBuf::from("./generated"); + + let bridges = vec!["src/ffi.rs"]; + + for bridge in &bridges { + println!("cargo:rerun-if-changed={bridge}"); + } + + swift_bridge_build::parse_bridges(bridges) + .write_all_concatenated(out_dir, env!("CARGO_PKG_NAME")); +} diff --git a/crates/egui-ios/generated/SwiftBridgeCore.h b/crates/egui-ios/generated/SwiftBridgeCore.h new file mode 100644 index 00000000000..b1b1f60ea07 --- /dev/null +++ b/crates/egui-ios/generated/SwiftBridgeCore.h @@ -0,0 +1,164 @@ +#include +#include +typedef struct RustStr { uint8_t* const start; uintptr_t len; } RustStr; +typedef struct __private__FfiSlice { void* const start; uintptr_t len; } __private__FfiSlice; +void* __swift_bridge__null_pointer(void); + + +typedef struct __private__OptionU8 { uint8_t val; bool is_some; } __private__OptionU8; +typedef struct __private__OptionI8 { int8_t val; bool is_some; } __private__OptionI8; +typedef struct __private__OptionU16 { uint16_t val; bool is_some; } __private__OptionU16; +typedef struct __private__OptionI16 { int16_t val; bool is_some; } __private__OptionI16; +typedef struct __private__OptionU32 { uint32_t val; bool is_some; } __private__OptionU32; +typedef struct __private__OptionI32 { int32_t val; bool is_some; } __private__OptionI32; +typedef struct __private__OptionU64 { uint64_t val; bool is_some; } __private__OptionU64; +typedef struct __private__OptionI64 { int64_t val; bool is_some; } __private__OptionI64; +typedef struct __private__OptionUsize { uintptr_t val; bool is_some; } __private__OptionUsize; +typedef struct __private__OptionIsize { intptr_t val; bool is_some; } __private__OptionIsize; +typedef struct __private__OptionF32 { float val; bool is_some; } __private__OptionF32; +typedef struct __private__OptionF64 { double val; bool is_some; } __private__OptionF64; +typedef struct __private__OptionBool { bool val; bool is_some; } __private__OptionBool; + +void* __swift_bridge__$Vec_u8$new(); +void __swift_bridge__$Vec_u8$_free(void* const vec); +uintptr_t __swift_bridge__$Vec_u8$len(void* const vec); +void __swift_bridge__$Vec_u8$push(void* const vec, uint8_t val); +__private__OptionU8 __swift_bridge__$Vec_u8$pop(void* const vec); +__private__OptionU8 __swift_bridge__$Vec_u8$get(void* const vec, uintptr_t index); +__private__OptionU8 __swift_bridge__$Vec_u8$get_mut(void* const vec, uintptr_t index); +uint8_t const * __swift_bridge__$Vec_u8$as_ptr(void* const vec); + +void* __swift_bridge__$Vec_u16$new(); +void __swift_bridge__$Vec_u16$_free(void* const vec); +uintptr_t __swift_bridge__$Vec_u16$len(void* const vec); +void __swift_bridge__$Vec_u16$push(void* const vec, uint16_t val); +__private__OptionU16 __swift_bridge__$Vec_u16$pop(void* const vec); +__private__OptionU16 __swift_bridge__$Vec_u16$get(void* const vec, uintptr_t index); +__private__OptionU16 __swift_bridge__$Vec_u16$get_mut(void* const vec, uintptr_t index); +uint16_t const * __swift_bridge__$Vec_u16$as_ptr(void* const vec); + +void* __swift_bridge__$Vec_u32$new(); +void __swift_bridge__$Vec_u32$_free(void* const vec); +uintptr_t __swift_bridge__$Vec_u32$len(void* const vec); +void __swift_bridge__$Vec_u32$push(void* const vec, uint32_t val); +__private__OptionU32 __swift_bridge__$Vec_u32$pop(void* const vec); +__private__OptionU32 __swift_bridge__$Vec_u32$get(void* const vec, uintptr_t index); +__private__OptionU32 __swift_bridge__$Vec_u32$get_mut(void* const vec, uintptr_t index); +uint32_t const * __swift_bridge__$Vec_u32$as_ptr(void* const vec); + +void* __swift_bridge__$Vec_u64$new(); +void __swift_bridge__$Vec_u64$_free(void* const vec); +uintptr_t __swift_bridge__$Vec_u64$len(void* const vec); +void __swift_bridge__$Vec_u64$push(void* const vec, uint64_t val); +__private__OptionU64 __swift_bridge__$Vec_u64$pop(void* const vec); +__private__OptionU64 __swift_bridge__$Vec_u64$get(void* const vec, uintptr_t index); +__private__OptionU64 __swift_bridge__$Vec_u64$get_mut(void* const vec, uintptr_t index); +uint64_t const * __swift_bridge__$Vec_u64$as_ptr(void* const vec); + +void* __swift_bridge__$Vec_usize$new(); +void __swift_bridge__$Vec_usize$_free(void* const vec); +uintptr_t __swift_bridge__$Vec_usize$len(void* const vec); +void __swift_bridge__$Vec_usize$push(void* const vec, uintptr_t val); +__private__OptionUsize __swift_bridge__$Vec_usize$pop(void* const vec); +__private__OptionUsize __swift_bridge__$Vec_usize$get(void* const vec, uintptr_t index); +__private__OptionUsize __swift_bridge__$Vec_usize$get_mut(void* const vec, uintptr_t index); +uintptr_t const * __swift_bridge__$Vec_usize$as_ptr(void* const vec); + +void* __swift_bridge__$Vec_i8$new(); +void __swift_bridge__$Vec_i8$_free(void* const vec); +uintptr_t __swift_bridge__$Vec_i8$len(void* const vec); +void __swift_bridge__$Vec_i8$push(void* const vec, int8_t val); +__private__OptionI8 __swift_bridge__$Vec_i8$pop(void* const vec); +__private__OptionI8 __swift_bridge__$Vec_i8$get(void* const vec, uintptr_t index); +__private__OptionI8 __swift_bridge__$Vec_i8$get_mut(void* const vec, uintptr_t index); +int8_t const * __swift_bridge__$Vec_i8$as_ptr(void* const vec); + +void* __swift_bridge__$Vec_i16$new(); +void __swift_bridge__$Vec_i16$_free(void* const vec); +uintptr_t __swift_bridge__$Vec_i16$len(void* const vec); +void __swift_bridge__$Vec_i16$push(void* const vec, int16_t val); +__private__OptionI16 __swift_bridge__$Vec_i16$pop(void* const vec); +__private__OptionI16 __swift_bridge__$Vec_i16$get(void* const vec, uintptr_t index); +__private__OptionI16 __swift_bridge__$Vec_i16$get_mut(void* const vec, uintptr_t index); +int16_t const * __swift_bridge__$Vec_i16$as_ptr(void* const vec); + +void* __swift_bridge__$Vec_i32$new(); +void __swift_bridge__$Vec_i32$_free(void* const vec); +uintptr_t __swift_bridge__$Vec_i32$len(void* const vec); +void __swift_bridge__$Vec_i32$push(void* const vec, int32_t val); +__private__OptionI32 __swift_bridge__$Vec_i32$pop(void* const vec); +__private__OptionI32 __swift_bridge__$Vec_i32$get(void* const vec, uintptr_t index); +__private__OptionI32 __swift_bridge__$Vec_i32$get_mut(void* const vec, uintptr_t index); +int32_t const * __swift_bridge__$Vec_i32$as_ptr(void* const vec); + +void* __swift_bridge__$Vec_i64$new(); +void __swift_bridge__$Vec_i64$_free(void* const vec); +uintptr_t __swift_bridge__$Vec_i64$len(void* const vec); +void __swift_bridge__$Vec_i64$push(void* const vec, int64_t val); +__private__OptionI64 __swift_bridge__$Vec_i64$pop(void* const vec); +__private__OptionI64 __swift_bridge__$Vec_i64$get(void* const vec, uintptr_t index); +__private__OptionI64 __swift_bridge__$Vec_i64$get_mut(void* const vec, uintptr_t index); +int64_t const * __swift_bridge__$Vec_i64$as_ptr(void* const vec); + +void* __swift_bridge__$Vec_isize$new(); +void __swift_bridge__$Vec_isize$_free(void* const vec); +uintptr_t __swift_bridge__$Vec_isize$len(void* const vec); +void __swift_bridge__$Vec_isize$push(void* const vec, intptr_t val); +__private__OptionIsize __swift_bridge__$Vec_isize$pop(void* const vec); +__private__OptionIsize __swift_bridge__$Vec_isize$get(void* const vec, uintptr_t index); +__private__OptionIsize __swift_bridge__$Vec_isize$get_mut(void* const vec, uintptr_t index); +intptr_t const * __swift_bridge__$Vec_isize$as_ptr(void* const vec); + +void* __swift_bridge__$Vec_bool$new(); +void __swift_bridge__$Vec_bool$_free(void* const vec); +uintptr_t __swift_bridge__$Vec_bool$len(void* const vec); +void __swift_bridge__$Vec_bool$push(void* const vec, bool val); +__private__OptionBool __swift_bridge__$Vec_bool$pop(void* const vec); +__private__OptionBool __swift_bridge__$Vec_bool$get(void* const vec, uintptr_t index); +__private__OptionBool __swift_bridge__$Vec_bool$get_mut(void* const vec, uintptr_t index); +bool const * __swift_bridge__$Vec_bool$as_ptr(void* const vec); + +void* __swift_bridge__$Vec_f32$new(); +void __swift_bridge__$Vec_f32$_free(void* const vec); +uintptr_t __swift_bridge__$Vec_f32$len(void* const vec); +void __swift_bridge__$Vec_f32$push(void* const vec, float val); +__private__OptionF32 __swift_bridge__$Vec_f32$pop(void* const vec); +__private__OptionF32 __swift_bridge__$Vec_f32$get(void* const vec, uintptr_t index); +__private__OptionF32 __swift_bridge__$Vec_f32$get_mut(void* const vec, uintptr_t index); +float const * __swift_bridge__$Vec_f32$as_ptr(void* const vec); + +void* __swift_bridge__$Vec_f64$new(); +void __swift_bridge__$Vec_f64$_free(void* const vec); +uintptr_t __swift_bridge__$Vec_f64$len(void* const vec); +void __swift_bridge__$Vec_f64$push(void* const vec, double val); +__private__OptionF64 __swift_bridge__$Vec_f64$pop(void* const vec); +__private__OptionF64 __swift_bridge__$Vec_f64$get(void* const vec, uintptr_t index); +__private__OptionF64 __swift_bridge__$Vec_f64$get_mut(void* const vec, uintptr_t index); +double const * __swift_bridge__$Vec_f64$as_ptr(void* const vec); + +#include +typedef struct RustString RustString; +void __swift_bridge__$RustString$_free(void* self); + +void* __swift_bridge__$Vec_RustString$new(void); +void __swift_bridge__$Vec_RustString$drop(void* vec_ptr); +void __swift_bridge__$Vec_RustString$push(void* vec_ptr, void* item_ptr); +void* __swift_bridge__$Vec_RustString$pop(void* vec_ptr); +void* __swift_bridge__$Vec_RustString$get(void* vec_ptr, uintptr_t index); +void* __swift_bridge__$Vec_RustString$get_mut(void* vec_ptr, uintptr_t index); +uintptr_t __swift_bridge__$Vec_RustString$len(void* vec_ptr); +void* __swift_bridge__$Vec_RustString$as_ptr(void* vec_ptr); + +void* __swift_bridge__$RustString$new(void); +void* __swift_bridge__$RustString$new_with_str(struct RustStr str); +uintptr_t __swift_bridge__$RustString$len(void* self); +struct RustStr __swift_bridge__$RustString$as_str(void* self); +struct RustStr __swift_bridge__$RustString$trim(void* self); +bool __swift_bridge__$RustStr$partial_eq(struct RustStr lhs, struct RustStr rhs); + + +void __swift_bridge__$call_boxed_fn_once_no_args_no_return(void* boxed_fnonce); +void __swift_bridge__$free_boxed_fn_once_no_args_no_return(void* boxed_fnonce); + + +struct __private__ResultPtrAndPtr { bool is_ok; void* ok_or_err; }; diff --git a/crates/egui-ios/generated/SwiftBridgeCore.swift b/crates/egui-ios/generated/SwiftBridgeCore.swift new file mode 100644 index 00000000000..fff4aa9a169 --- /dev/null +++ b/crates/egui-ios/generated/SwiftBridgeCore.swift @@ -0,0 +1,1342 @@ +import Foundation + +extension RustString { + public func toString() -> String { + let str = self.as_str() + let string = str.toString() + + return string + } +} + +extension RustStr { + func toBufferPointer() -> UnsafeBufferPointer { + let bytes = UnsafeBufferPointer(start: self.start, count: Int(self.len)) + return bytes + } + + public func toString() -> String { + let bytes = self.toBufferPointer() + return String(bytes: bytes, encoding: .utf8)! + } +} +extension RustStr: Identifiable { + public var id: String { + self.toString() + } +} +extension RustStr: Equatable { + public static func == (lhs: RustStr, rhs: RustStr) -> Bool { + return __swift_bridge__$RustStr$partial_eq(lhs, rhs); + } +} + +public protocol IntoRustString { + func intoRustString() -> RustString; +} + +extension String: IntoRustString { + public func intoRustString() -> RustString { + // TODO: When passing an owned Swift std String to Rust we've being wasteful here in that + // we're creating a RustString (which involves Boxing a Rust std::string::String) + // only to unbox it back into a String once it gets to the Rust side. + // + // A better approach would be to pass a RustStr to the Rust side and then have Rust + // call `.to_string()` on the RustStr. + RustString(self) + } +} + +extension RustString: IntoRustString { + public func intoRustString() -> RustString { + self + } +} + +/// If the String is Some: +/// Safely get a scoped pointer to the String and then call the callback with a RustStr +/// that uses that pointer. +/// +/// If the String is None: +/// Call the callback with a RustStr that has a null pointer. +/// The Rust side will know to treat this as `None`. +func optionalStringIntoRustString(_ string: Optional) -> RustString? { + if let val = string { + return val.intoRustString() + } else { + return nil + } +} + +/// Used to safely get a pointer to a sequence of utf8 bytes, represented as a `RustStr`. +/// +/// For example, the Swift `String` implementation of the `ToRustStr` protocol does the following: +/// 1. Use Swift's `String.utf8.withUnsafeBufferPointer` to get a pointer to the strings underlying +/// utf8 bytes. +/// 2. Construct a `RustStr` that points to these utf8 bytes. This is safe because `withUnsafeBufferPointer` +/// guarantees that the buffer pointer will be valid for the duration of the `withUnsafeBufferPointer` +/// callback. +/// 3. Pass the `RustStr` to the closure that was passed into `RustStr.toRustStr`. +public protocol ToRustStr { + func toRustStr (_ withUnsafeRustStr: (RustStr) -> T) -> T; +} + +extension String: ToRustStr { + /// Safely get a scoped pointer to the String and then call the callback with a RustStr + /// that uses that pointer. + public func toRustStr (_ withUnsafeRustStr: (RustStr) -> T) -> T { + return self.utf8CString.withUnsafeBufferPointer({ bufferPtr in + let rustStr = RustStr( + start: UnsafeMutableRawPointer(mutating: bufferPtr.baseAddress!).assumingMemoryBound(to: UInt8.self), + // Subtract 1 because of the null termination character at the end + len: UInt(bufferPtr.count - 1) + ) + return withUnsafeRustStr(rustStr) + }) + } +} + +extension RustStr: ToRustStr { + public func toRustStr (_ withUnsafeRustStr: (RustStr) -> T) -> T { + return withUnsafeRustStr(self) + } +} + +func optionalRustStrToRustStr(_ str: Optional, _ withUnsafeRustStr: (RustStr) -> T) -> T { + if let val = str { + return val.toRustStr(withUnsafeRustStr) + } else { + return withUnsafeRustStr(RustStr(start: nil, len: 0)) + } +} +public class RustVec { + var ptr: UnsafeMutableRawPointer + var isOwned: Bool = true + + public init(ptr: UnsafeMutableRawPointer) { + self.ptr = ptr + } + + public init() { + ptr = T.vecOfSelfNew() + isOwned = true + } + + public func push (value: T) { + T.vecOfSelfPush(vecPtr: ptr, value: value) + } + + public func pop () -> Optional { + T.vecOfSelfPop(vecPtr: ptr) + } + + public func get(index: UInt) -> Optional { + T.vecOfSelfGet(vecPtr: ptr, index: index) + } + + public func as_ptr() -> UnsafePointer { + UnsafePointer(OpaquePointer(T.vecOfSelfAsPtr(vecPtr: ptr))) + } + + /// Rust returns a UInt, but we cast to an Int because many Swift APIs such as + /// `ForEach(0..rustVec.len())` expect Int. + public func len() -> Int { + Int(T.vecOfSelfLen(vecPtr: ptr)) + } + + deinit { + if isOwned { + T.vecOfSelfFree(vecPtr: ptr) + } + } +} + +extension RustVec: Sequence { + public func makeIterator() -> RustVecIterator { + return RustVecIterator(self) + } +} + +public struct RustVecIterator: IteratorProtocol { + var rustVec: RustVec + var index: UInt = 0 + + init (_ rustVec: RustVec) { + self.rustVec = rustVec + } + + public mutating func next() -> T.SelfRef? { + let val = rustVec.get(index: index) + index += 1 + return val + } +} + +extension RustVec: Collection { + public typealias Index = Int + + public func index(after i: Int) -> Int { + i + 1 + } + + public subscript(position: Int) -> T.SelfRef { + self.get(index: UInt(position))! + } + + public var startIndex: Int { + 0 + } + + public var endIndex: Int { + self.len() + } +} + +extension RustVec: RandomAccessCollection {} + +extension UnsafeBufferPointer { + func toFfiSlice () -> __private__FfiSlice { + __private__FfiSlice(start: UnsafeMutablePointer(mutating: self.baseAddress), len: UInt(self.count)) + } +} + +public protocol Vectorizable { + associatedtype SelfRef + associatedtype SelfRefMut + + static func vecOfSelfNew() -> UnsafeMutableRawPointer; + + static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) + + static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) + + static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional + + static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional + + static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional + + static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer + + static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt +} + +extension UInt8: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_u8$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_u8$_free(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_u8$push(vecPtr, value) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let val = __swift_bridge__$Vec_u8$pop(vecPtr) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_u8$get(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_u8$get_mut(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_u8$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_u8$len(vecPtr) + } +} + +extension UInt16: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_u16$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_u16$_free(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_u16$push(vecPtr, value) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let val = __swift_bridge__$Vec_u16$pop(vecPtr) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_u16$get(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_u16$get_mut(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_u16$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_u16$len(vecPtr) + } +} + +extension UInt32: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_u32$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_u32$_free(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_u32$push(vecPtr, value) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let val = __swift_bridge__$Vec_u32$pop(vecPtr) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_u32$get(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_u32$get_mut(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_u32$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_u32$len(vecPtr) + } +} + +extension UInt64: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_u64$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_u64$_free(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_u64$push(vecPtr, value) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let val = __swift_bridge__$Vec_u64$pop(vecPtr) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_u64$get(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_u64$get_mut(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_u64$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_u64$len(vecPtr) + } +} + +extension UInt: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_usize$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_usize$_free(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_usize$push(vecPtr, value) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let val = __swift_bridge__$Vec_usize$pop(vecPtr) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_usize$get(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_usize$get_mut(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_usize$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_usize$len(vecPtr) + } +} + +extension Int8: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_i8$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_i8$_free(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_i8$push(vecPtr, value) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let val = __swift_bridge__$Vec_i8$pop(vecPtr) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_i8$get(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_i8$get_mut(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_i8$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_i8$len(vecPtr) + } +} + +extension Int16: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_i16$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_i16$_free(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_i16$push(vecPtr, value) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let val = __swift_bridge__$Vec_i16$pop(vecPtr) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_i16$get(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_i16$get_mut(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_i16$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_i16$len(vecPtr) + } +} + +extension Int32: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_i32$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_i32$_free(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_i32$push(vecPtr, value) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let val = __swift_bridge__$Vec_i32$pop(vecPtr) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_i32$get(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_i32$get_mut(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_i32$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_i32$len(vecPtr) + } +} + +extension Int64: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_i64$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_i64$_free(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_i64$push(vecPtr, value) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let val = __swift_bridge__$Vec_i64$pop(vecPtr) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_i64$get(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_i64$get_mut(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_i64$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_i64$len(vecPtr) + } +} + +extension Int: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_isize$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_isize$_free(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_isize$push(vecPtr, value) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let val = __swift_bridge__$Vec_isize$pop(vecPtr) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_isize$get(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_isize$get_mut(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_isize$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_isize$len(vecPtr) + } +} + +extension Bool: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_bool$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_bool$_free(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_bool$push(vecPtr, value) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let val = __swift_bridge__$Vec_bool$pop(vecPtr) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_bool$get(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_bool$get_mut(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_bool$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_bool$len(vecPtr) + } +} + +extension Float: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_f32$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_f32$_free(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_f32$push(vecPtr, value) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let val = __swift_bridge__$Vec_f32$pop(vecPtr) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_f32$get(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_f32$get_mut(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_f32$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_f32$len(vecPtr) + } +} + +extension Double: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_f64$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_f64$_free(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_f64$push(vecPtr, value) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let val = __swift_bridge__$Vec_f64$pop(vecPtr) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_f64$get(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let val = __swift_bridge__$Vec_f64$get_mut(vecPtr, index) + if val.is_some { + return val.val + } else { + return nil + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_f64$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_f64$len(vecPtr) + } +} + +protocol SwiftBridgeGenericFreer { + func rust_free(); +} + +protocol SwiftBridgeGenericCopyTypeFfiRepr {} + +public class RustString: RustStringRefMut { + var isOwned: Bool = true + + public override init(ptr: UnsafeMutableRawPointer) { + super.init(ptr: ptr) + } + + deinit { + if isOwned { + __swift_bridge__$RustString$_free(ptr) + } + } +} + +/// Tested in: +/// SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/ResultTests.swift: +/// `func testSwiftCallRustReturnsResultString()` +extension RustString: Error {} + +// THREAD SAFETY: `RustString`, `RustStringRef` and `RustStringRefMut` are safe to send across threads as long as the +// ownership and aliasing rules are followed. +// This is because the underlying Rust `std::string::String`, `&str` and `&mut str` are all `Send+Sync`. +// See the `Safety` chapter in the book for more information about memory and thread safety rules. +// +// For now we have implemented `Sendable` for `RustString`. If users need `RustStringRef` or `RustStringRefMut` to +// implement `Sendable` then we can implement those as well. +// +// Tested in: +// `SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/SendableTests.swift` +// `func testSendableRustString()` +extension RustString: @unchecked Sendable {} + +extension RustString { + public convenience init() { + self.init(ptr: __swift_bridge__$RustString$new()) + } + + public convenience init(_ str: GenericToRustStr) { + self.init(ptr: str.toRustStr({ strAsRustStr in + __swift_bridge__$RustString$new_with_str(strAsRustStr) + })) + } +} +public class RustStringRefMut: RustStringRef { + public override init(ptr: UnsafeMutableRawPointer) { + super.init(ptr: ptr) + } +} +public class RustStringRef { + var ptr: UnsafeMutableRawPointer + + public init(ptr: UnsafeMutableRawPointer) { + self.ptr = ptr + } +} +extension RustStringRef { + public func len() -> UInt { + __swift_bridge__$RustString$len(ptr) + } + + public func as_str() -> RustStr { + __swift_bridge__$RustString$as_str(ptr) + } + + public func trim() -> RustStr { + __swift_bridge__$RustString$trim(ptr) + } +} +extension RustString: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_RustString$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_RustString$drop(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: RustString) { + __swift_bridge__$Vec_RustString$push(vecPtr, {value.isOwned = false; return value.ptr;}()) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let pointer = __swift_bridge__$Vec_RustString$pop(vecPtr) + if pointer == nil { + return nil + } else { + return (RustString(ptr: pointer!) as! Self) + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let pointer = __swift_bridge__$Vec_RustString$get(vecPtr, index) + if pointer == nil { + return nil + } else { + return RustStringRef(ptr: pointer!) + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let pointer = __swift_bridge__$Vec_RustString$get_mut(vecPtr, index) + if pointer == nil { + return nil + } else { + return RustStringRefMut(ptr: pointer!) + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_RustString$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_RustString$len(vecPtr) + } +} + + +public class __private__RustFnOnceCallbackNoArgsNoRet { + var ptr: UnsafeMutableRawPointer + var called = false + + init(ptr: UnsafeMutableRawPointer) { + self.ptr = ptr + } + + deinit { + if !called { + __swift_bridge__$free_boxed_fn_once_no_args_no_return(ptr) + } + } + + func call() { + if called { + fatalError("Cannot call a Rust FnOnce function twice") + } + called = true + return __swift_bridge__$call_boxed_fn_once_no_args_no_return(ptr) + } +} + + +public enum RustResult { + case Ok(T) + case Err(E) +} + +extension RustResult { + func ok() -> T? { + switch self { + case .Ok(let ok): + return ok + case .Err(_): + return nil + } + } + + func err() -> E? { + switch self { + case .Ok(_): + return nil + case .Err(let err): + return err + } + } + + func toResult() -> Result + where E: Error { + switch self { + case .Ok(let ok): + return .success(ok) + case .Err(let err): + return .failure(err) + } + } +} + + +extension __private__OptionU8 { + func intoSwiftRepr() -> Optional { + if self.is_some { + return self.val + } else { + return nil + } + } + + init(_ val: Optional) { + if let val = val { + self = Self(val: val, is_some: true) + } else { + self = Self(val: 123, is_some: false) + } + } +} +extension Optional where Wrapped == UInt8 { + func intoFfiRepr() -> __private__OptionU8 { + __private__OptionU8(self) + } +} + +extension __private__OptionI8 { + func intoSwiftRepr() -> Optional { + if self.is_some { + return self.val + } else { + return nil + } + } + + init(_ val: Optional) { + if let val = val { + self = Self(val: val, is_some: true) + } else { + self = Self(val: 123, is_some: false) + } + } +} +extension Optional where Wrapped == Int8 { + func intoFfiRepr() -> __private__OptionI8 { + __private__OptionI8(self) + } +} + +extension __private__OptionU16 { + func intoSwiftRepr() -> Optional { + if self.is_some { + return self.val + } else { + return nil + } + } + + init(_ val: Optional) { + if let val = val { + self = Self(val: val, is_some: true) + } else { + self = Self(val: 123, is_some: false) + } + } +} +extension Optional where Wrapped == UInt16 { + func intoFfiRepr() -> __private__OptionU16 { + __private__OptionU16(self) + } +} + +extension __private__OptionI16 { + func intoSwiftRepr() -> Optional { + if self.is_some { + return self.val + } else { + return nil + } + } + + init(_ val: Optional) { + if let val = val { + self = Self(val: val, is_some: true) + } else { + self = Self(val: 123, is_some: false) + } + } +} +extension Optional where Wrapped == Int16 { + func intoFfiRepr() -> __private__OptionI16 { + __private__OptionI16(self) + } +} + +extension __private__OptionU32 { + func intoSwiftRepr() -> Optional { + if self.is_some { + return self.val + } else { + return nil + } + } + + init(_ val: Optional) { + if let val = val { + self = Self(val: val, is_some: true) + } else { + self = Self(val: 123, is_some: false) + } + } +} +extension Optional where Wrapped == UInt32 { + func intoFfiRepr() -> __private__OptionU32 { + __private__OptionU32(self) + } +} + +extension __private__OptionI32 { + func intoSwiftRepr() -> Optional { + if self.is_some { + return self.val + } else { + return nil + } + } + + init(_ val: Optional) { + if let val = val { + self = Self(val: val, is_some: true) + } else { + self = Self(val: 123, is_some: false) + } + } +} +extension Optional where Wrapped == Int32 { + func intoFfiRepr() -> __private__OptionI32 { + __private__OptionI32(self) + } +} + +extension __private__OptionU64 { + func intoSwiftRepr() -> Optional { + if self.is_some { + return self.val + } else { + return nil + } + } + + init(_ val: Optional) { + if let val = val { + self = Self(val: val, is_some: true) + } else { + self = Self(val: 123, is_some: false) + } + } +} +extension Optional where Wrapped == UInt64 { + func intoFfiRepr() -> __private__OptionU64 { + __private__OptionU64(self) + } +} + +extension __private__OptionI64 { + func intoSwiftRepr() -> Optional { + if self.is_some { + return self.val + } else { + return nil + } + } + + init(_ val: Optional) { + if let val = val { + self = Self(val: val, is_some: true) + } else { + self = Self(val: 123, is_some: false) + } + } +} +extension Optional where Wrapped == Int64 { + func intoFfiRepr() -> __private__OptionI64 { + __private__OptionI64(self) + } +} + +extension __private__OptionUsize { + func intoSwiftRepr() -> Optional { + if self.is_some { + return self.val + } else { + return nil + } + } + + init(_ val: Optional) { + if let val = val { + self = Self(val: val, is_some: true) + } else { + self = Self(val: 123, is_some: false) + } + } +} +extension Optional where Wrapped == UInt { + func intoFfiRepr() -> __private__OptionUsize { + __private__OptionUsize(self) + } +} + +extension __private__OptionIsize { + func intoSwiftRepr() -> Optional { + if self.is_some { + return self.val + } else { + return nil + } + } + + init(_ val: Optional) { + if let val = val { + self = Self(val: val, is_some: true) + } else { + self = Self(val: 123, is_some: false) + } + } +} +extension Optional where Wrapped == Int { + func intoFfiRepr() -> __private__OptionIsize { + __private__OptionIsize(self) + } +} + +extension __private__OptionF32 { + func intoSwiftRepr() -> Optional { + if self.is_some { + return self.val + } else { + return nil + } + } + + init(_ val: Optional) { + if let val = val { + self = Self(val: val, is_some: true) + } else { + self = Self(val: 123.4, is_some: false) + } + } +} +extension Optional where Wrapped == Float { + func intoFfiRepr() -> __private__OptionF32 { + __private__OptionF32(self) + } +} + +extension __private__OptionF64 { + func intoSwiftRepr() -> Optional { + if self.is_some { + return self.val + } else { + return nil + } + } + + init(_ val: Optional) { + if let val = val { + self = Self(val: val, is_some: true) + } else { + self = Self(val: 123.4, is_some: false) + } + } +} +extension Optional where Wrapped == Double { + func intoFfiRepr() -> __private__OptionF64 { + __private__OptionF64(self) + } +} + +extension __private__OptionBool { + func intoSwiftRepr() -> Optional { + if self.is_some { + return self.val + } else { + return nil + } + } + + init(_ val: Optional) { + if let val = val { + self = Self(val: val, is_some: true) + } else { + self = Self(val: false, is_some: false) + } + } +} +extension Optional where Wrapped == Bool { + func intoFfiRepr() -> __private__OptionBool { + __private__OptionBool(self) + } +} diff --git a/crates/egui-ios/generated/egui-ios/egui-ios.h b/crates/egui-ios/generated/egui-ios/egui-ios.h new file mode 100644 index 00000000000..149d929cdcb --- /dev/null +++ b/crates/egui-ios/generated/egui-ios/egui-ios.h @@ -0,0 +1,63 @@ +// File automatically generated by swift-bridge. +#include +#include +typedef struct InputEvent InputEvent; +void __swift_bridge__$InputEvent$_free(void* self); + +void* __swift_bridge__$Vec_InputEvent$new(void); +void __swift_bridge__$Vec_InputEvent$drop(void* vec_ptr); +void __swift_bridge__$Vec_InputEvent$push(void* vec_ptr, void* item_ptr); +void* __swift_bridge__$Vec_InputEvent$pop(void* vec_ptr); +void* __swift_bridge__$Vec_InputEvent$get(void* vec_ptr, uintptr_t index); +void* __swift_bridge__$Vec_InputEvent$get_mut(void* vec_ptr, uintptr_t index); +uintptr_t __swift_bridge__$Vec_InputEvent$len(void* vec_ptr); +void* __swift_bridge__$Vec_InputEvent$as_ptr(void* vec_ptr); + +typedef struct OutputState OutputState; +void __swift_bridge__$OutputState$_free(void* self); + +void* __swift_bridge__$Vec_OutputState$new(void); +void __swift_bridge__$Vec_OutputState$drop(void* vec_ptr); +void __swift_bridge__$Vec_OutputState$push(void* vec_ptr, void* item_ptr); +void* __swift_bridge__$Vec_OutputState$pop(void* vec_ptr); +void* __swift_bridge__$Vec_OutputState$get(void* vec_ptr, uintptr_t index); +void* __swift_bridge__$Vec_OutputState$get_mut(void* vec_ptr, uintptr_t index); +uintptr_t __swift_bridge__$Vec_OutputState$len(void* vec_ptr); +void* __swift_bridge__$Vec_OutputState$as_ptr(void* vec_ptr); + +typedef struct CursorIcon CursorIcon; +void __swift_bridge__$CursorIcon$_free(void* self); + +void* __swift_bridge__$Vec_CursorIcon$new(void); +void __swift_bridge__$Vec_CursorIcon$drop(void* vec_ptr); +void __swift_bridge__$Vec_CursorIcon$push(void* vec_ptr, void* item_ptr); +void* __swift_bridge__$Vec_CursorIcon$pop(void* vec_ptr); +void* __swift_bridge__$Vec_CursorIcon$get(void* vec_ptr, uintptr_t index); +void* __swift_bridge__$Vec_CursorIcon$get_mut(void* vec_ptr, uintptr_t index); +uintptr_t __swift_bridge__$Vec_CursorIcon$len(void* vec_ptr); +void* __swift_bridge__$Vec_CursorIcon$as_ptr(void* vec_ptr); + +void* __swift_bridge__$InputEvent$from_pointer_moved(float x, float y); +void* __swift_bridge__$InputEvent$from_mouse_wheel(float x, float y); +void* __swift_bridge__$InputEvent$from_left_mouse_down(float x, float y, bool pressed); +void* __swift_bridge__$InputEvent$from_right_mouse_down(float x, float y, bool pressed); +void* __swift_bridge__$InputEvent$from_window_focused(bool focused); +void* __swift_bridge__$InputEvent$from_scene_phase_changed(uint8_t phase); +void* __swift_bridge__$InputEvent$from_text_commit(void* text); +void* __swift_bridge__$InputEvent$from_ime_preedit(void* text); +void* __swift_bridge__$InputEvent$from_keyboard_visibility(bool visible); +void* __swift_bridge__$InputEvent$from_virtual_key(uint8_t key_code, bool pressed); +void* __swift_bridge__$OutputState$get_cursor_icon(void* self); +bool __swift_bridge__$OutputState$wants_keyboard(void* self); +bool __swift_bridge__$OutputState$has_ime_rect(void* self); +float __swift_bridge__$OutputState$get_ime_rect_x(void* self); +float __swift_bridge__$OutputState$get_ime_rect_y(void* self); +float __swift_bridge__$OutputState$get_ime_rect_width(void* self); +float __swift_bridge__$OutputState$get_ime_rect_height(void* self); +bool __swift_bridge__$CursorIcon$is_default(void* self); +bool __swift_bridge__$CursorIcon$is_pointing_hand(void* self); +bool __swift_bridge__$CursorIcon$is_resize_horizontal(void* self); +bool __swift_bridge__$CursorIcon$is_resize_vertical(void* self); +bool __swift_bridge__$CursorIcon$is_text(void* self); + + diff --git a/crates/egui-ios/generated/egui-ios/egui-ios.swift b/crates/egui-ios/generated/egui-ios/egui-ios.swift new file mode 100644 index 00000000000..ebbd55015ab --- /dev/null +++ b/crates/egui-ios/generated/egui-ios/egui-ios.swift @@ -0,0 +1,318 @@ + +public class InputEvent: InputEventRefMut { + var isOwned: Bool = true + + public override init(ptr: UnsafeMutableRawPointer) { + super.init(ptr: ptr) + } + + deinit { + if isOwned { + __swift_bridge__$InputEvent$_free(ptr) + } + } +} +extension InputEvent { + class public func from_pointer_moved(_ x: Float, _ y: Float) -> InputEvent { + InputEvent(ptr: __swift_bridge__$InputEvent$from_pointer_moved(x, y)) + } + + class public func from_mouse_wheel(_ x: Float, _ y: Float) -> InputEvent { + InputEvent(ptr: __swift_bridge__$InputEvent$from_mouse_wheel(x, y)) + } + + class public func from_left_mouse_down(_ x: Float, _ y: Float, _ pressed: Bool) -> InputEvent { + InputEvent(ptr: __swift_bridge__$InputEvent$from_left_mouse_down(x, y, pressed)) + } + + class public func from_right_mouse_down(_ x: Float, _ y: Float, _ pressed: Bool) -> InputEvent { + InputEvent(ptr: __swift_bridge__$InputEvent$from_right_mouse_down(x, y, pressed)) + } + + class public func from_window_focused(_ focused: Bool) -> InputEvent { + InputEvent(ptr: __swift_bridge__$InputEvent$from_window_focused(focused)) + } + + class public func from_scene_phase_changed(_ phase: UInt8) -> InputEvent { + InputEvent(ptr: __swift_bridge__$InputEvent$from_scene_phase_changed(phase)) + } + + class public func from_text_commit(_ text: GenericIntoRustString) -> InputEvent { + InputEvent(ptr: __swift_bridge__$InputEvent$from_text_commit({ let rustString = text.intoRustString(); rustString.isOwned = false; return rustString.ptr }())) + } + + class public func from_ime_preedit(_ text: GenericIntoRustString) -> InputEvent { + InputEvent(ptr: __swift_bridge__$InputEvent$from_ime_preedit({ let rustString = text.intoRustString(); rustString.isOwned = false; return rustString.ptr }())) + } + + class public func from_keyboard_visibility(_ visible: Bool) -> InputEvent { + InputEvent(ptr: __swift_bridge__$InputEvent$from_keyboard_visibility(visible)) + } + + class public func from_virtual_key(_ key_code: UInt8, _ pressed: Bool) -> InputEvent { + InputEvent(ptr: __swift_bridge__$InputEvent$from_virtual_key(key_code, pressed)) + } +} +public class InputEventRefMut: InputEventRef { + public override init(ptr: UnsafeMutableRawPointer) { + super.init(ptr: ptr) + } +} +public class InputEventRef { + var ptr: UnsafeMutableRawPointer + + public init(ptr: UnsafeMutableRawPointer) { + self.ptr = ptr + } +} +extension InputEvent: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_InputEvent$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_InputEvent$drop(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: InputEvent) { + __swift_bridge__$Vec_InputEvent$push(vecPtr, {value.isOwned = false; return value.ptr;}()) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let pointer = __swift_bridge__$Vec_InputEvent$pop(vecPtr) + if pointer == nil { + return nil + } else { + return (InputEvent(ptr: pointer!) as! Self) + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let pointer = __swift_bridge__$Vec_InputEvent$get(vecPtr, index) + if pointer == nil { + return nil + } else { + return InputEventRef(ptr: pointer!) + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let pointer = __swift_bridge__$Vec_InputEvent$get_mut(vecPtr, index) + if pointer == nil { + return nil + } else { + return InputEventRefMut(ptr: pointer!) + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_InputEvent$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_InputEvent$len(vecPtr) + } +} + + +public class OutputState: OutputStateRefMut { + var isOwned: Bool = true + + public override init(ptr: UnsafeMutableRawPointer) { + super.init(ptr: ptr) + } + + deinit { + if isOwned { + __swift_bridge__$OutputState$_free(ptr) + } + } +} +public class OutputStateRefMut: OutputStateRef { + public override init(ptr: UnsafeMutableRawPointer) { + super.init(ptr: ptr) + } +} +public class OutputStateRef { + var ptr: UnsafeMutableRawPointer + + public init(ptr: UnsafeMutableRawPointer) { + self.ptr = ptr + } +} +extension OutputStateRef { + public func get_cursor_icon() -> CursorIconRef { + CursorIconRef(ptr: __swift_bridge__$OutputState$get_cursor_icon(ptr)) + } + + public func wants_keyboard() -> Bool { + __swift_bridge__$OutputState$wants_keyboard(ptr) + } + + public func has_ime_rect() -> Bool { + __swift_bridge__$OutputState$has_ime_rect(ptr) + } + + public func get_ime_rect_x() -> Float { + __swift_bridge__$OutputState$get_ime_rect_x(ptr) + } + + public func get_ime_rect_y() -> Float { + __swift_bridge__$OutputState$get_ime_rect_y(ptr) + } + + public func get_ime_rect_width() -> Float { + __swift_bridge__$OutputState$get_ime_rect_width(ptr) + } + + public func get_ime_rect_height() -> Float { + __swift_bridge__$OutputState$get_ime_rect_height(ptr) + } +} +extension OutputState: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_OutputState$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_OutputState$drop(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: OutputState) { + __swift_bridge__$Vec_OutputState$push(vecPtr, {value.isOwned = false; return value.ptr;}()) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let pointer = __swift_bridge__$Vec_OutputState$pop(vecPtr) + if pointer == nil { + return nil + } else { + return (OutputState(ptr: pointer!) as! Self) + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let pointer = __swift_bridge__$Vec_OutputState$get(vecPtr, index) + if pointer == nil { + return nil + } else { + return OutputStateRef(ptr: pointer!) + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let pointer = __swift_bridge__$Vec_OutputState$get_mut(vecPtr, index) + if pointer == nil { + return nil + } else { + return OutputStateRefMut(ptr: pointer!) + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_OutputState$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_OutputState$len(vecPtr) + } +} + + +public class CursorIcon: CursorIconRefMut { + var isOwned: Bool = true + + public override init(ptr: UnsafeMutableRawPointer) { + super.init(ptr: ptr) + } + + deinit { + if isOwned { + __swift_bridge__$CursorIcon$_free(ptr) + } + } +} +public class CursorIconRefMut: CursorIconRef { + public override init(ptr: UnsafeMutableRawPointer) { + super.init(ptr: ptr) + } +} +public class CursorIconRef { + var ptr: UnsafeMutableRawPointer + + public init(ptr: UnsafeMutableRawPointer) { + self.ptr = ptr + } +} +extension CursorIconRef { + public func is_default() -> Bool { + __swift_bridge__$CursorIcon$is_default(ptr) + } + + public func is_pointing_hand() -> Bool { + __swift_bridge__$CursorIcon$is_pointing_hand(ptr) + } + + public func is_resize_horizontal() -> Bool { + __swift_bridge__$CursorIcon$is_resize_horizontal(ptr) + } + + public func is_resize_vertical() -> Bool { + __swift_bridge__$CursorIcon$is_resize_vertical(ptr) + } + + public func is_text() -> Bool { + __swift_bridge__$CursorIcon$is_text(ptr) + } +} +extension CursorIcon: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_CursorIcon$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_CursorIcon$drop(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: CursorIcon) { + __swift_bridge__$Vec_CursorIcon$push(vecPtr, {value.isOwned = false; return value.ptr;}()) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let pointer = __swift_bridge__$Vec_CursorIcon$pop(vecPtr) + if pointer == nil { + return nil + } else { + return (CursorIcon(ptr: pointer!) as! Self) + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let pointer = __swift_bridge__$Vec_CursorIcon$get(vecPtr, index) + if pointer == nil { + return nil + } else { + return CursorIconRef(ptr: pointer!) + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let pointer = __swift_bridge__$Vec_CursorIcon$get_mut(vecPtr, index) + if pointer == nil { + return nil + } else { + return CursorIconRefMut(ptr: pointer!) + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_CursorIcon$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_CursorIcon$len(vecPtr) + } +} + + + diff --git a/crates/egui-ios/src/ffi.rs b/crates/egui-ios/src/ffi.rs new file mode 100644 index 00000000000..213374556ab --- /dev/null +++ b/crates/egui-ios/src/ffi.rs @@ -0,0 +1,70 @@ +use crate::input::InputEvent; +use crate::output::{CursorIcon, OutputState}; + +#[swift_bridge::bridge] +pub mod ffi { + extern "Rust" { + type InputEvent; + + #[swift_bridge(associated_to = InputEvent)] + fn from_pointer_moved(x: f32, y: f32) -> InputEvent; + + #[swift_bridge(associated_to = InputEvent)] + fn from_mouse_wheel(x: f32, y: f32) -> InputEvent; + + #[swift_bridge(associated_to = InputEvent)] + fn from_left_mouse_down(x: f32, y: f32, pressed: bool) -> InputEvent; + + #[swift_bridge(associated_to = InputEvent)] + fn from_right_mouse_down(x: f32, y: f32, pressed: bool) -> InputEvent; + + #[swift_bridge(associated_to = InputEvent)] + fn from_window_focused(focused: bool) -> InputEvent; + + // Scene phase changed: 0 = background, 1 = inactive, 2 = active + #[swift_bridge(associated_to = InputEvent)] + fn from_scene_phase_changed(phase: u8) -> InputEvent; + + // Text input from native keyboard (after autocomplete/autocorrect) + #[swift_bridge(associated_to = InputEvent)] + fn from_text_commit(text: String) -> InputEvent; + + // IME preedit/composition text (e.g., partial CJK input) + #[swift_bridge(associated_to = InputEvent)] + fn from_ime_preedit(text: String) -> InputEvent; + + // Virtual keyboard visibility changed + #[swift_bridge(associated_to = InputEvent)] + fn from_keyboard_visibility(visible: bool) -> InputEvent; + + // Virtual key press (backspace=0, enter=1, tab=2, escape=3, arrows=4-7) + #[swift_bridge(associated_to = InputEvent)] + fn from_virtual_key(key_code: u8, pressed: bool) -> InputEvent; + } + + extern "Rust" { + type OutputState; + + fn get_cursor_icon(&self) -> &CursorIcon; + + // Whether egui wants the keyboard to be visible + fn wants_keyboard(&self) -> bool; + + // IME cursor rect for keyboard positioning + fn has_ime_rect(&self) -> bool; + fn get_ime_rect_x(&self) -> f32; + fn get_ime_rect_y(&self) -> f32; + fn get_ime_rect_width(&self) -> f32; + fn get_ime_rect_height(&self) -> f32; + } + + extern "Rust" { + type CursorIcon; + + fn is_default(&self) -> bool; + fn is_pointing_hand(&self) -> bool; + fn is_resize_horizontal(&self) -> bool; + fn is_resize_vertical(&self) -> bool; + fn is_text(&self) -> bool; + } +} diff --git a/crates/egui-ios/src/input.rs b/crates/egui-ios/src/input.rs new file mode 100644 index 00000000000..d69809aa46d --- /dev/null +++ b/crates/egui-ios/src/input.rs @@ -0,0 +1,151 @@ +use egui::{Event, Modifiers}; + +/// Input events from Swift/iOS to egui +pub enum InputEvent { + /// Pointer/touch moved to position + PointerMoved(f32, f32), + /// Mouse wheel / scroll gesture + MouseWheel(f32, f32), + /// Primary touch/click (x, y, pressed) + LeftMouseDown(f32, f32, bool), + /// Secondary touch/click (x, y, pressed) + RightMouseDown(f32, f32, bool), + /// Window focus changed + WindowFocused(bool), + /// Scene phase changed (iOS UIScene lifecycle) + /// phase: 0 = background, 1 = inactive, 2 = active + ScenePhaseChanged(u8), + /// Committed text from keyboard (after autocomplete/autocorrect) + TextCommit(String), + /// IME preedit/composition text (e.g., partial CJK input) + /// Empty string clears the preedit + ImePreedit(String), + /// Virtual keyboard shown/hidden + KeyboardVisibility(bool), + /// Key pressed (for special keys like backspace, return) + /// key_code: 0=Backspace, 1=Enter, 2=Tab, 3=Escape, 4-7=Arrows + VirtualKey(u8, bool), +} + +/// iOS scene phase - maps to SwiftUI ScenePhase +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ScenePhase { + /// App is in background, not visible + Background = 0, + /// App is visible but not receiving events (e.g., during alerts) + Inactive = 1, + /// App is in foreground and receiving events + Active = 2, +} + +impl From for ScenePhase { + fn from(value: u8) -> Self { + match value { + 0 => Self::Background, + 1 => Self::Inactive, + _ => Self::Active, + } + } +} + +impl InputEvent { + /// Convert to egui Event, returning None for events handled separately + pub fn into_egui_event(self) -> Option { + match self { + InputEvent::PointerMoved(x, y) => Some(Event::PointerMoved(egui::Pos2::new(x, y))), + InputEvent::MouseWheel(x, y) => Some(Event::MouseWheel { + unit: egui::MouseWheelUnit::Point, + delta: egui::vec2(x, y), + phase: egui::TouchPhase::Move, + modifiers: Modifiers::default(), + }), + InputEvent::LeftMouseDown(x, y, pressed) => Some(Event::PointerButton { + pos: egui::Pos2::new(x, y), + button: egui::PointerButton::Primary, + pressed, + modifiers: Modifiers::default(), + }), + InputEvent::RightMouseDown(x, y, pressed) => Some(Event::PointerButton { + pos: egui::Pos2::new(x, y), + button: egui::PointerButton::Secondary, + pressed, + modifiers: Modifiers::default(), + }), + InputEvent::WindowFocused(focused) => Some(Event::WindowFocused(focused)), + InputEvent::ScenePhaseChanged(phase) => { + Some(Event::WindowFocused(phase == ScenePhase::Active as u8)) + } + InputEvent::TextCommit(text) => Some(Event::Text(text)), + InputEvent::ImePreedit(text) => { + Some(Event::Ime(egui::ImeEvent::Preedit(text))) + } + InputEvent::KeyboardVisibility(_) => None, // Handled separately + InputEvent::VirtualKey(key_code, pressed) => virtual_key_to_event(key_code, pressed), + } + } +} + +/// Map virtual key codes to egui Key events +fn virtual_key_to_event(key_code: u8, pressed: bool) -> Option { + let key = match key_code { + 0 => egui::Key::Backspace, + 1 => egui::Key::Enter, + 2 => egui::Key::Tab, + 3 => egui::Key::Escape, + 4 => egui::Key::ArrowUp, + 5 => egui::Key::ArrowDown, + 6 => egui::Key::ArrowLeft, + 7 => egui::Key::ArrowRight, + _ => return None, + }; + Some(Event::Key { + key, + physical_key: None, + pressed, + repeat: false, + modifiers: Modifiers::default(), + }) +} + +// FFI factory methods called from Swift +impl InputEvent { + pub fn from_pointer_moved(x: f32, y: f32) -> Self { + Self::PointerMoved(x, y) + } + + pub fn from_mouse_wheel(x: f32, y: f32) -> Self { + Self::MouseWheel(x, y) + } + + pub fn from_left_mouse_down(x: f32, y: f32, pressed: bool) -> Self { + Self::LeftMouseDown(x, y, pressed) + } + + pub fn from_right_mouse_down(x: f32, y: f32, pressed: bool) -> Self { + Self::RightMouseDown(x, y, pressed) + } + + pub fn from_window_focused(focused: bool) -> Self { + Self::WindowFocused(focused) + } + + pub fn from_scene_phase_changed(phase: u8) -> Self { + Self::ScenePhaseChanged(phase) + } + + pub fn from_text_commit(text: String) -> Self { + Self::TextCommit(text) + } + + pub fn from_ime_preedit(text: String) -> Self { + Self::ImePreedit(text) + } + + pub fn from_keyboard_visibility(visible: bool) -> Self { + Self::KeyboardVisibility(visible) + } + + pub fn from_virtual_key(key_code: u8, pressed: bool) -> Self { + Self::VirtualKey(key_code, pressed) + } +} diff --git a/crates/egui-ios/src/lib.rs b/crates/egui-ios/src/lib.rs new file mode 100644 index 00000000000..2b08509d1f7 --- /dev/null +++ b/crates/egui-ios/src/lib.rs @@ -0,0 +1,39 @@ +//! iOS FFI bindings for egui via swift-bridge +//! +//! This crate provides Swift-compatible types for embedding egui in iOS apps: +//! +//! - [`InputEvent`] - Touch, keyboard, and lifecycle events from Swift to egui +//! - [`OutputState`] - Cursor, keyboard, and IME state from egui to Swift +//! - [`CursorIcon`] - Cursor icons mapped to iOS equivalents +//! +//! ## Usage +//! +//! Add this crate to your iOS Rust library and include the generated Swift bindings: +//! +//! ```rust,ignore +//! use egui_ios::{InputEvent, OutputState}; +//! +//! // Convert input events to egui events +//! let egui_events: Vec = input_events +//! .into_iter() +//! .filter_map(|e| e.into_egui_event()) +//! .collect(); +//! +//! // After running egui, create output state +//! let output = OutputState::with_keyboard_state( +//! cursor_icon.into(), +//! ctx.wants_keyboard_input(), +//! platform_output.ime.as_ref().map(|ime| ime.rect), +//! ); +//! ``` +//! +//! ## Swift Integration +//! +//! See the `SWIFTUI_EMBEDDING.md` guide for complete Swift integration examples. + +mod ffi; +mod input; +mod output; + +pub use input::{InputEvent, ScenePhase}; +pub use output::{CursorIcon, OutputState}; diff --git a/crates/egui-ios/src/output.rs b/crates/egui-ios/src/output.rs new file mode 100644 index 00000000000..2cb88b4cc3c --- /dev/null +++ b/crates/egui-ios/src/output.rs @@ -0,0 +1,109 @@ +/// Output state from egui to Swift/iOS +pub struct OutputState { + cursor_icon: CursorIcon, + /// Whether egui wants the keyboard visible (text field focused) + wants_keyboard: bool, + /// IME cursor area for keyboard positioning (x, y, width, height in points) + ime_rect: Option<(f32, f32, f32, f32)>, +} + +impl OutputState { + pub fn new(cursor_icon: CursorIcon) -> Self { + Self { + cursor_icon, + wants_keyboard: false, + ime_rect: None, + } + } + + pub fn with_keyboard_state( + cursor_icon: CursorIcon, + wants_keyboard: bool, + ime_rect: Option, + ) -> Self { + Self { + cursor_icon, + wants_keyboard, + ime_rect: ime_rect.map(|r| (r.min.x, r.min.y, r.width(), r.height())), + } + } + + pub fn get_cursor_icon(&self) -> &CursorIcon { + &self.cursor_icon + } + + pub fn wants_keyboard(&self) -> bool { + self.wants_keyboard + } + + pub fn get_ime_rect(&self) -> Option<(f32, f32, f32, f32)> { + self.ime_rect + } + + // FFI accessors for swift-bridge (can't return Option across FFI) + pub fn has_ime_rect(&self) -> bool { + self.ime_rect.is_some() + } + + pub fn get_ime_rect_x(&self) -> f32 { + self.ime_rect.map(|r| r.0).unwrap_or(0.0) + } + + pub fn get_ime_rect_y(&self) -> f32 { + self.ime_rect.map(|r| r.1).unwrap_or(0.0) + } + + pub fn get_ime_rect_width(&self) -> f32 { + self.ime_rect.map(|r| r.2).unwrap_or(0.0) + } + + pub fn get_ime_rect_height(&self) -> f32 { + self.ime_rect.map(|r| r.3).unwrap_or(0.0) + } +} + +/// Cursor icon for iOS (subset of egui icons that map to iOS) +pub enum CursorIcon { + Default, + PointingHand, + ResizeHorizontal, + ResizeVertical, + Text, +} + +impl From for CursorIcon { + fn from(cursor_icon: egui::CursorIcon) -> Self { + match cursor_icon { + egui::CursorIcon::Default => Self::Default, + egui::CursorIcon::PointingHand => Self::PointingHand, + egui::CursorIcon::ResizeHorizontal | egui::CursorIcon::ResizeColumn => { + Self::ResizeHorizontal + } + egui::CursorIcon::ResizeVertical | egui::CursorIcon::ResizeRow => Self::ResizeVertical, + egui::CursorIcon::Text => Self::Text, + _ => Self::Default, + } + } +} + +impl CursorIcon { + pub fn is_default(&self) -> bool { + matches!(self, Self::Default) + } + + pub fn is_pointing_hand(&self) -> bool { + matches!(self, Self::PointingHand) + } + + pub fn is_resize_horizontal(&self) -> bool { + matches!(self, Self::ResizeHorizontal) + } + + pub fn is_resize_vertical(&self) -> bool { + matches!(self, Self::ResizeVertical) + } + + pub fn is_text(&self) -> bool { + matches!(self, Self::Text) + } +} diff --git a/xtask/src/ios.rs b/xtask/src/ios.rs new file mode 100644 index 00000000000..1fc7c7b7a6c --- /dev/null +++ b/xtask/src/ios.rs @@ -0,0 +1,332 @@ +//! iOS app bundling functionality +//! +//! Creates .app bundles for iOS simulator or device targets. + +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use crate::DynError; + +/// Target platform for iOS builds +#[derive(Debug, Clone, Copy)] +enum IosTarget { + /// Physical iOS device (aarch64-apple-ios) + Device, + /// iOS Simulator (aarch64-apple-ios-sim) + Simulator, +} + +impl IosTarget { + fn triple(&self) -> &'static str { + match self { + Self::Device => "aarch64-apple-ios", + Self::Simulator => "aarch64-apple-ios-sim", + } + } +} + +/// Configuration for iOS bundle +struct BundleConfig { + package: String, + target: IosTarget, + release: bool, + install: bool, + launch: bool, + simulator_name: Option, +} + +pub fn bundle_ios(args: &[&str]) -> Result<(), DynError> { + let config = parse_args(args)?; + + // Step 1: Build the Rust code + println!("Building {} for {}...", config.package, config.target.triple()); + build_rust(&config)?; + + // Step 2: Create the app bundle + println!("Creating app bundle..."); + let app_path = create_bundle(&config)?; + println!("Created: {}", app_path.display()); + + // Step 3: Install to simulator (if requested) + if config.install { + let sim_name = config.simulator_name.as_deref().unwrap_or("iPhone 15 Pro"); + println!("Installing to simulator '{}'...", sim_name); + install_to_simulator(&app_path, sim_name)?; + + // Step 4: Launch (if requested) + if config.launch { + let bundle_id = format!("com.egui.{}", config.package.replace('_', "-")); + println!("Launching {}...", bundle_id); + launch_on_simulator(&bundle_id, sim_name)?; + } + } + + println!("Done!"); + Ok(()) +} + +fn parse_args(args: &[&str]) -> Result { + let mut package = None; + let mut target = IosTarget::Simulator; + let mut release = false; + let mut install = false; + let mut launch = false; + let mut simulator_name = None; + + let mut i = 0; + while i < args.len() { + match args[i] { + "-h" | "--help" => { + print_ios_help(); + std::process::exit(0); + } + "-p" | "--package" => { + i += 1; + package = args.get(i).map(|s| s.to_string()); + } + "--sim" | "--simulator" => { + target = IosTarget::Simulator; + } + "--device" => { + target = IosTarget::Device; + } + "-r" | "--release" => { + release = true; + } + "--install" => { + install = true; + } + "--launch" => { + install = true; // launch implies install + launch = true; + } + "--simulator-name" => { + i += 1; + simulator_name = args.get(i).map(|s| s.to_string()); + } + other => { + // If no flag, treat as package name + if !other.starts_with('-') && package.is_none() { + package = Some(other.to_string()); + } else { + return Err(format!("Unknown argument: {}", other).into()); + } + } + } + i += 1; + } + + let package = package.ok_or("Package name required. Use -p or provide as argument.")?; + + Ok(BundleConfig { + package, + target, + release, + install, + launch, + simulator_name, + }) +} + +fn print_ios_help() { + println!( + r#" +cargo xtask bundle-ios - Build and bundle an iOS app + +USAGE: + cargo xtask bundle-ios [OPTIONS] + cargo xtask bundle-ios -p [OPTIONS] + +ARGS: + Name of the package to bundle (e.g., hello_ios) + +OPTIONS: + -p, --package Package to bundle + --sim, --simulator Build for iOS Simulator (default) + --device Build for physical iOS device + -r, --release Build in release mode + --install Install to simulator after building + --launch Install and launch on simulator + --simulator-name Simulator to use (default: "iPhone 15 Pro") + -h, --help Show this help message + +EXAMPLES: + cargo xtask bundle-ios hello_ios --sim + cargo xtask bundle-ios -p hello_ios --launch + cargo xtask bundle-ios hello_ios --device --release +"# + ); +} + +fn build_rust(config: &BundleConfig) -> Result<(), DynError> { + let mut cmd = Command::new("cargo"); + cmd.arg("build") + .arg("-p") + .arg(&config.package) + .arg("--target") + .arg(config.target.triple()); + + if config.release { + cmd.arg("--release"); + } + + let status = cmd.status()?; + if !status.success() { + return Err(format!("Cargo build failed with status: {}", status).into()); + } + + Ok(()) +} + +fn create_bundle(config: &BundleConfig) -> Result { + let profile = if config.release { "release" } else { "debug" }; + let target_dir = Path::new("target") + .join(config.target.triple()) + .join(profile); + + // Find the binary + let binary_path = target_dir.join(&config.package); + if !binary_path.exists() { + return Err(format!( + "Binary not found at {}. Make sure the package builds a binary.", + binary_path.display() + ).into()); + } + + // Create bundle directory + let bundle_dir = target_dir.join("bundle").join("ios"); + let app_name = config.package.replace('_', " "); + let app_name = to_title_case(&app_name); + let app_path = bundle_dir.join(format!("{}.app", app_name)); + + // Clean and recreate + if app_path.exists() { + fs::remove_dir_all(&app_path)?; + } + fs::create_dir_all(&app_path)?; + + // Copy binary + let dest_binary = app_path.join(&config.package); + fs::copy(&binary_path, &dest_binary)?; + + // Create Info.plist + let bundle_id = format!("com.egui.{}", config.package.replace('_', "-")); + let info_plist = create_info_plist(&app_name, &bundle_id, &config.package); + let plist_path = app_path.join("Info.plist"); + fs::write(&plist_path, info_plist)?; + + // Copy icon if it exists + let icon_path = Path::new("crates/eframe/data/icon.png"); + if icon_path.exists() { + fs::copy(icon_path, app_path.join("AppIcon.png"))?; + } + + Ok(app_path) +} + +fn create_info_plist(app_name: &str, bundle_id: &str, executable: &str) -> String { + format!( + r#" + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + {app_name} + CFBundleExecutable + {executable} + CFBundleIdentifier + {bundle_id} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + {app_name} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + MinimumOSVersion + 13.0 + UIDeviceFamily + + 1 + 2 + + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + +"# + ) +} + +fn install_to_simulator(app_path: &Path, simulator_name: &str) -> Result<(), DynError> { + // Boot the simulator if needed + let _ = Command::new("xcrun") + .args(["simctl", "boot", simulator_name]) + .output(); + + // Install the app + let status = Command::new("xcrun") + .args(["simctl", "install", simulator_name]) + .arg(app_path) + .status()?; + + if !status.success() { + return Err(format!("Failed to install app to simulator: {}", status).into()); + } + + // Open Simulator.app + let _ = Command::new("open") + .args(["-a", "Simulator"]) + .status(); + + Ok(()) +} + +fn launch_on_simulator(bundle_id: &str, simulator_name: &str) -> Result<(), DynError> { + let status = Command::new("xcrun") + .args(["simctl", "launch", simulator_name, bundle_id]) + .status()?; + + if !status.success() { + return Err(format!("Failed to launch app: {}", status).into()); + } + + Ok(()) +} + +/// Convert "hello_world" or "hello world" to "Hello World" +fn to_title_case(s: &str) -> String { + s.split_whitespace() + .map(|word| { + let mut chars = word.chars(); + match chars.next() { + None => String::new(), + Some(first) => first.to_uppercase().chain(chars).collect(), + } + }) + .collect::>() + .join(" ") +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 0f16c545427..283e46ec15b 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -5,6 +5,7 @@ #![allow(clippy::exit)] mod deny; +mod ios; pub(crate) mod utils; type DynError = Box; @@ -23,6 +24,7 @@ fn try_main() -> Result<(), DynError> { match args.as_slice() { &[] | &["-h"] | &["--help"] => print_help(), &["deny", ..] => deny::deny(&args[1..])?, + &["bundle-ios", ..] => ios::bundle_ios(&args[1..])?, c => Err(format!("Invalid arguments {c:?}"))?, } Ok(()) @@ -33,7 +35,8 @@ fn print_help() { xtask help Subcommands - deny: Run cargo-deny for all targets + deny: Run cargo-deny for all targets + bundle-ios: Build and bundle an iOS app for simulator or device Options -h, --help: print help and exit