#![cfg(target_os = "ios")]
#![cfg(feature = "ios-uikit-ffi")]
use objc2::rc::Retained;
use objc2::MainThreadMarker;
use objc2_foundation::{CGPoint, CGRect, CGSize, NSString};
use objc2_ui_kit::{
UIButton, UIButtonType, UIColor, UILabel, UIPickerView, UIProgressView, UISlider, UISwitch,
UITableView, UITableViewStyle, UITextField, UIView, UIViewController, UIWindow,
};
use std::collections::HashMap;
use std::sync::LazyLock;
use std::sync::Mutex;
#[derive(Clone, Copy)]
struct NativePtr(*mut std::ffi::c_void);
unsafe impl Send for NativePtr {}
unsafe impl Sync for NativePtr {}
static NATIVE_VIEWS: LazyLock<Mutex<HashMap<u64, NativePtr>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));
pub(crate) fn store_native_view(widget_id: u64, view: *mut std::ffi::c_void) {
NATIVE_VIEWS.lock().unwrap().insert(widget_id, NativePtr(view));
}
pub(crate) fn get_native_view(widget_id: u64) -> Option<*mut std::ffi::c_void> {
NATIVE_VIEWS.lock().unwrap().get(&widget_id).map(|p| p.0)
}
pub(crate) fn remove_native_view(widget_id: u64) {
NATIVE_VIEWS.lock().unwrap().remove(&widget_id);
}
fn make_rect(x: i32, y: i32, width: u32, height: u32) -> CGRect {
CGRect::new(
CGPoint::new(x as f64, y as f64),
CGSize::new(width.max(1) as f64, height.max(1) as f64),
)
}
pub(crate) fn create_ui_window(
mtm: MainThreadMarker,
title: &str,
x: i32,
y: i32,
width: u32,
height: u32,
) -> Retained<UIWindow> {
let frame = make_rect(0, 0, width, height);
let window = unsafe { UIWindow::initWithFrame(mtm.alloc(), frame) };
window.setBackgroundColor(UIColor::white());
let vc = unsafe { UIViewController::initWithNibName_bundle(mtm.alloc(), None, None) };
window.setRootViewController(Some(&vc));
window.makeKeyAndVisible();
window.setAccessibilityLabel(&NSString::from_str(title));
window
}
pub(crate) fn create_ui_button(
mtm: MainThreadMarker,
text: &str,
x: i32,
y: i32,
width: u32,
height: u32,
) -> Retained<UIButton> {
let frame = make_rect(x, y, width, height);
let button = unsafe { UIButton::initWithFrame(mtm.alloc(), frame) };
button.setTitle(&NSString::from_str(text));
button.setButtonType(UIButtonType::System);
button
}
pub(crate) fn create_ui_label(
mtm: MainThreadMarker,
text: &str,
x: i32,
y: i32,
width: u32,
height: u32,
) -> Retained<UILabel> {
let frame = make_rect(x, y, width, height);
let label = unsafe { UILabel::initWithFrame(mtm.alloc(), frame) };
label.setText(&NSString::from_str(text));
label
}
pub(crate) fn create_ui_checkbox(
mtm: MainThreadMarker,
text: &str,
x: i32,
y: i32,
width: u32,
height: u32,
) -> Retained<UISwitch> {
let frame = make_rect(x, y, width, height);
let switch_ctl = unsafe { UISwitch::initWithFrame(mtm.alloc(), frame) };
switch_ctl.setAccessibilityLabel(&NSString::from_str(text));
switch_ctl
}
pub(crate) fn create_ui_line_edit(
mtm: MainThreadMarker,
text: &str,
x: i32,
y: i32,
width: u32,
height: u32,
) -> Retained<UITextField> {
let frame = make_rect(x, y, width, height);
let text_field = unsafe { UITextField::initWithFrame(mtm.alloc(), frame) };
text_field.setText(&NSString::from_str(text));
text_field
}
pub(crate) fn create_ui_radio_button(
mtm: MainThreadMarker,
text: &str,
x: i32,
y: i32,
width: u32,
height: u32,
) -> Retained<UIButton> {
let frame = make_rect(x, y, width, height);
let button = unsafe { UIButton::initWithFrame(mtm.alloc(), frame) };
button.setTitle(&NSString::from_str(text));
button.setButtonType(UIButtonType::System);
button
}
pub(crate) fn create_ui_slider(
mtm: MainThreadMarker,
x: i32,
y: i32,
width: u32,
height: u32,
) -> Retained<UISlider> {
let frame = make_rect(x, y, width, height);
let slider = unsafe { UISlider::initWithFrame(mtm.alloc(), frame) };
slider.setMinimumValue(0.0);
slider.setMaximumValue(100.0);
slider
}
pub(crate) fn create_ui_progress_bar(
mtm: MainThreadMarker,
x: i32,
y: i32,
width: u32,
height: u32,
) -> Retained<UIProgressView> {
let frame = make_rect(x, y, width, height);
let progress = unsafe { UIProgressView::initWithFrame(mtm.alloc(), frame) };
progress
}
pub(crate) fn create_ui_combo_box(
mtm: MainThreadMarker,
x: i32,
y: i32,
width: u32,
height: u32,
) -> Retained<UIPickerView> {
let frame = make_rect(x, y, width, height);
let picker = unsafe { UIPickerView::initWithFrame(mtm.alloc(), frame) };
picker
}
pub(crate) fn create_ui_list_box(
mtm: MainThreadMarker,
x: i32,
y: i32,
width: u32,
height: u32,
) -> Retained<UITableView> {
let frame = make_rect(x, y, width, height);
let table =
unsafe { UITableView::initWithFrame_style(mtm.alloc(), frame, UITableViewStyle::Plain) };
table
}