use crate::components::text::WuiText;
use crate::id::WuiId;
use crate::reactive::{WuiBinding, WuiComputed};
use crate::{AnyView, WuiAnyView, WuiLabel, WuiStr};
use alloc::vec::Vec;
use jiff::civil::{Date, DateTime};
use waterui::text::styled::StyledStr;
use waterui::{
Color, Str,
component::{
slider::SliderConfig,
stepper::StepperConfig,
text_field::{KeyboardType, ResolvedTextFieldConfig},
toggle::{ToggleConfig, ToggleStyle},
},
};
use waterui_core::{Native, id::Id};
use waterui_form::picker::color::ColorPickerConfig;
use waterui_form::picker::date::{DatePickerConfig, DatePickerType};
use waterui_form::picker::multi_date::MultiDatePickerConfig;
use waterui_form::picker::{PickerConfig, PickerItem, PickerStyle};
use waterui_form::secure::{Secure, SecureFieldConfig};
into_ffi! {KeyboardType, non_exhaustive, pub enum WuiKeyboardType {
Text,
Email,
URL,
Number,
PhoneNumber
}}
#[repr(C)]
#[derive(Debug)]
pub struct WuiTextField {
pub label: WuiLabel,
pub value: *mut WuiBinding<StyledStr>,
pub prompt: WuiText,
pub keyboard: WuiKeyboardType,
pub selection_menu: *mut crate::views::WuiAnyViews,
pub line_limit: usize,
}
impl IntoFFI for ResolvedTextFieldConfig {
type FFI = WuiTextField;
fn into_ffi(self) -> Self::FFI {
WuiTextField {
label: self.label.into_ffi(),
value: self.value.into_ffi(),
prompt: self.prompt.into_ffi(),
keyboard: self.keyboard.into_ffi(),
selection_menu: crate::menu_items_views(self.selection_menu),
line_limit: self.line_limit.map_or(0, core::num::NonZeroUsize::get),
}
}
}
into_ffi! {ToggleStyle, non_exhaustive, pub enum WuiToggleStyle {
Automatic,
Switch,
Checkbox,
}}
into_ffi! {ToggleConfig,
pub struct WuiToggle {
label: WuiLabel,
toggle: *mut WuiBinding<bool>,
style: WuiToggleStyle,
}
}
#[repr(C)]
#[derive(Debug)]
pub struct WuiRange<T> {
pub start: T,
pub end: T,
}
into_ffi! {SliderConfig,
pub struct WuiSlider {
label: WuiLabel,
min_value_label: *mut WuiAnyView,
max_value_label: *mut WuiAnyView,
range: WuiRange<f64>,
value: *mut WuiBinding<f64>,
}
}
into_ffi! {StepperConfig,
pub struct WuiStepper {
value: *mut WuiBinding<i32>,
step: *mut WuiComputed<i32>,
label: WuiLabel,
value_formatter: *mut WuiComputed<StyledStr>,
range: WuiRange<i32>,
}
}
use crate::IntoFFI;
use core::ops::RangeInclusive;
impl IntoFFI for RangeInclusive<f64> {
type FFI = WuiRange<f64>;
fn into_ffi(self) -> Self::FFI {
WuiRange {
start: *self.start(),
end: *self.end(),
}
}
}
impl IntoFFI for RangeInclusive<i32> {
type FFI = WuiRange<i32>;
fn into_ffi(self) -> Self::FFI {
WuiRange {
start: *self.start(),
end: *self.end(),
}
}
}
ffi_view!(ResolvedTextFieldConfig, WuiTextField, text_field);
ffi_view!(ToggleConfig, WuiToggle, toggle);
ffi_view!(SliderConfig, WuiSlider, slider);
ffi_view!(StepperConfig, WuiStepper, stepper);
ffi_view!(ColorPickerConfig, WuiColorPicker, color_picker);
ffi_view!(PickerConfig, WuiPicker, picker);
ffi_view!(SecureFieldConfig, WuiSecureField, secure_field);
into_ffi! {PickerStyle, non_exhaustive, pub enum WuiPickerStyle {
Automatic,
Menu,
Radio,
Segmented,
}}
#[repr(C)]
#[derive(Debug)]
pub struct WuiPicker {
pub label: WuiLabel,
pub items: *mut crate::views::WuiAnyViews,
pub selection: *mut WuiBinding<Id>,
pub style: WuiPickerStyle,
}
impl IntoFFI for PickerConfig {
type FFI = WuiPicker;
fn into_ffi(self) -> Self::FFI {
WuiPicker {
label: self.label.into_ffi(),
items: crate::views::signal_vec_views(
self.items,
|items, index| items[index].tag,
|item| AnyView::new(Native::new(ResolvedPickerItem(item))),
),
selection: self.selection.into_ffi(),
style: self.style.into_ffi(),
}
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct ResolvedPickerItem(PickerItem<Id>);
waterui_core::raw_view!(ResolvedPickerItem);
#[repr(C)]
#[derive(Debug)]
pub struct WuiPickerItem {
pub tag: WuiId,
pub label: *mut WuiComputed<StyledStr>,
}
impl IntoFFI for ResolvedPickerItem {
type FFI = WuiPickerItem;
fn into_ffi(self) -> Self::FFI {
WuiPickerItem {
tag: self.0.tag.into_ffi(),
label: self.0.content.content().into_ffi(),
}
}
}
ffi_view!(ResolvedPickerItem, WuiPickerItem, picker_item);
into_ffi! {ColorPickerConfig,
pub struct WuiColorPicker {
label: WuiLabel,
value: *mut WuiBinding<Color>,
support_alpha: bool,
support_hdr: bool,
}
}
impl IntoFFI for Secure {
type FFI = WuiStr;
fn into_ffi(self) -> Self::FFI {
use alloc::string::String;
let owned = String::from(self.expose());
Str::from(owned).into_ffi()
}
}
into_ffi! {SecureFieldConfig,
pub struct WuiSecureField {
label: WuiLabel,
value: *mut WuiBinding<Secure>,
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Default)]
pub struct WuiDate {
pub year: i32,
pub month: u8,
pub day: u8,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct WuiDateTime {
pub year: i32,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
}
impl IntoFFI for Date {
type FFI = WuiDate;
fn into_ffi(self) -> Self::FFI {
WuiDate {
year: i32::from(self.year()),
month: self.month().try_into().expect("jiff month must fit in u8"),
day: self.day().try_into().expect("jiff day must fit in u8"),
}
}
}
impl IntoFFI for DateTime {
type FFI = WuiDateTime;
fn into_ffi(self) -> Self::FFI {
WuiDateTime {
year: i32::from(self.year()),
month: self.month().try_into().expect("jiff month must fit in u8"),
day: self.day().try_into().expect("jiff day must fit in u8"),
hour: self.hour().try_into().expect("jiff hour must fit in u8"),
minute: self
.minute()
.try_into()
.expect("jiff minute must fit in u8"),
second: self
.second()
.try_into()
.expect("jiff second must fit in u8"),
}
}
}
impl crate::IntoRust for WuiDate {
type Rust = Date;
unsafe fn into_rust(self) -> Self::Rust {
let year = i16::try_from(self.year)
.expect("invalid year received from native DatePicker FFI bridge");
let month = i8::try_from(self.month)
.expect("invalid month received from native DatePicker FFI bridge");
let day =
i8::try_from(self.day).expect("invalid day received from native DatePicker FFI bridge");
Date::new(year, month, day).unwrap_or_else(|_| {
panic!(
"invalid date received from native DatePicker FFI bridge: year={}, month={}, day={}",
self.year, self.month, self.day
)
})
}
}
impl crate::IntoRust for WuiDateTime {
type Rust = DateTime;
unsafe fn into_rust(self) -> Self::Rust {
let year = i16::try_from(self.year)
.expect("invalid year received from native DatePicker FFI bridge");
let month = i8::try_from(self.month)
.expect("invalid month received from native DatePicker FFI bridge");
let day =
i8::try_from(self.day).expect("invalid day received from native DatePicker FFI bridge");
let hour = i8::try_from(self.hour)
.expect("invalid hour received from native DatePicker FFI bridge");
let minute = i8::try_from(self.minute)
.expect("invalid minute received from native DatePicker FFI bridge");
let second = i8::try_from(self.second)
.expect("invalid second received from native DatePicker FFI bridge");
DateTime::new(year, month, day, hour, minute, second, 0).unwrap_or_else(|_| {
panic!(
"invalid date-time received from native DatePicker FFI bridge: year={}, month={}, day={}, hour={}, minute={}, second={}",
self.year, self.month, self.day, self.hour, self.minute, self.second
)
})
}
}
impl IntoFFI for core::ops::RangeInclusive<Date> {
type FFI = WuiRange<WuiDate>;
fn into_ffi(self) -> Self::FFI {
WuiRange {
start: (*self.start()).into_ffi(),
end: (*self.end()).into_ffi(),
}
}
}
impl IntoFFI for core::ops::RangeInclusive<DateTime> {
type FFI = WuiRange<WuiDateTime>;
fn into_ffi(self) -> Self::FFI {
WuiRange {
start: (*self.start()).into_ffi(),
end: (*self.end()).into_ffi(),
}
}
}
into_ffi! {DatePickerType, pub enum WuiDatePickerType {
Date,
HourAndMinute,
HourMinuteAndSecond,
DateHourAndMinute,
DateHourMinuteAndSecond,
}}
into_ffi! {DatePickerConfig,
pub struct WuiDatePicker {
label: WuiLabel,
value: *mut WuiBinding<DateTime>,
range: WuiRange<WuiDateTime>,
ty: WuiDatePickerType,
}
}
ffi_view!(DatePickerConfig, WuiDatePicker, date_picker);
into_ffi! {MultiDatePickerConfig,
pub struct WuiMultiDatePicker {
label: WuiLabel,
value: *mut WuiBinding<Vec<Date>>,
range: WuiRange<WuiDate>,
decorated: *mut WuiComputed<Vec<Date>>,
}
}
ffi_view!(MultiDatePickerConfig, WuiMultiDatePicker, multi_date_picker);