Another project
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 3.2 kB View raw
1use core::fmt::Display; 2use core::marker::PhantomData; 3 4use crate::frame::FrameCtx; 5use crate::input::NamedKey; 6use crate::layout::LayoutRect; 7use crate::strings::StringKey; 8use crate::widget_id::WidgetId; 9 10use super::keys::{TakeKey, take_key}; 11use super::paint::WidgetPaint; 12use super::text_input::{ 13 Clipboard, TextInput, TextInputResponse, TextInputState, TextInputValidation, show_text_input, 14}; 15 16pub trait ParsedValue: Sized + Clone + PartialEq { 17 type Error: Display + Clone + PartialEq; 18 19 fn parse(text: &str) -> Result<Self, Self::Error>; 20} 21 22pub struct ParsedInput<'state, T: ParsedValue> { 23 pub id: WidgetId, 24 pub rect: LayoutRect, 25 pub placeholder: StringKey, 26 pub state: &'state mut TextInputState, 27 pub disabled: bool, 28 _ty: PhantomData<T>, 29} 30 31impl<'state, T: ParsedValue> ParsedInput<'state, T> { 32 #[must_use] 33 pub fn new( 34 id: WidgetId, 35 rect: LayoutRect, 36 placeholder: StringKey, 37 state: &'state mut TextInputState, 38 ) -> Self { 39 Self { 40 id, 41 rect, 42 placeholder, 43 state, 44 disabled: false, 45 _ty: PhantomData, 46 } 47 } 48 49 #[must_use] 50 pub fn disabled(self, disabled: bool) -> Self { 51 Self { disabled, ..self } 52 } 53} 54 55#[derive(Clone, Debug, PartialEq)] 56pub struct ParsedInputResponse<T: ParsedValue> { 57 pub interaction: crate::hit_test::Interaction, 58 pub value: Option<T>, 59 pub committed: Option<T>, 60 pub paint: Vec<WidgetPaint>, 61 pub error: Option<T::Error>, 62} 63 64struct ParsedValidator<T: ParsedValue>(PhantomData<T>); 65 66impl<T: ParsedValue> TextInputValidation for ParsedValidator<T> { 67 type Error = T::Error; 68 fn validate(&self, text: &str) -> Result<(), Self::Error> { 69 if text.trim().is_empty() { 70 return Ok(()); 71 } 72 T::parse(text).map(drop) 73 } 74} 75 76#[must_use] 77pub fn show_parsed_input<T: ParsedValue, C: Clipboard + ?Sized>( 78 ctx: &mut FrameCtx<'_>, 79 input: ParsedInput<'_, T>, 80 clipboard: &mut C, 81) -> ParsedInputResponse<T> { 82 let ParsedInput { 83 id, 84 rect, 85 placeholder, 86 state, 87 disabled, 88 .. 89 } = input; 90 let was_focused = state.was_focused; 91 let live_focused = ctx.is_focused(id); 92 let commit_via_enter = !disabled 93 && live_focused 94 && take_key(ctx.input, &[TakeKey::named(NamedKey::Enter)]).is_some(); 95 let TextInputResponse { 96 interaction, 97 error, 98 paint, 99 .. 100 } = { 101 let widget = TextInput { 102 id, 103 rect, 104 placeholder, 105 state: &mut *state, 106 disabled, 107 validator: ParsedValidator::<T>(PhantomData), 108 }; 109 show_text_input(ctx, widget, clipboard) 110 }; 111 let value = if state.text.trim().is_empty() { 112 None 113 } else { 114 T::parse(&state.text).ok() 115 }; 116 let lost_focus = was_focused && !live_focused; 117 let committed = if commit_via_enter || lost_focus { 118 value.clone() 119 } else { 120 None 121 }; 122 ParsedInputResponse { 123 interaction, 124 value, 125 committed, 126 paint, 127 error, 128 } 129}