1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::{
    unpack_named_slots, widget,
    widget::{
        component::interactive::button::{button, ButtonProps, ButtonSettingsProps, TextChange},
        WidgetId,
    },
    widget_component, widget_hook,
};
use serde::{Deserialize, Serialize};

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct InputFieldProps {
    #[serde(default)]
    pub text: String,
    #[serde(default)]
    pub cursor_position: usize,
    #[serde(default)]
    pub allow_new_line: bool,
}
implement_props_data!(InputFieldProps);

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct InputFieldMessage {
    #[serde(default)]
    pub sender: WidgetId,
    #[serde(default)]
    pub data: InputFieldProps,
}

widget_hook! {
    use_input_field(life_cycle) {
        life_cycle.mount(|context| {
            let props = context.props.read_cloned_or_default::<InputFieldProps>();
            drop(context.state.write(props));
        });

        life_cycle.change(|context| {
            let ButtonSettingsProps { disabled, notify } = context.props.read_cloned_or_default();
            let props = context.props.read_cloned_or_default::<ButtonProps>();
            let mut data = match context.state.read::<InputFieldProps>() {
                Ok(state) => state.clone(),
                Err(_) => InputFieldProps::default(),
            };
            if !disabled && !props.text.is_empty() {
                for change in &props.text {
                    match change {
                        TextChange::InsertCharacter(c) => {
                            if !c.is_control() {
                                data.cursor_position = data.cursor_position.min(data.text.len());
                                data.text.insert(data.cursor_position, *c);
                                data.cursor_position += 1;
                            }
                        }
                        TextChange::MoveCursorLeft => if data.cursor_position > 0 {
                            data.cursor_position -= 1;
                        }
                        TextChange::MoveCursorRight => if data.cursor_position < data.text.len() {
                            data.cursor_position += 1;
                        }
                        TextChange::MoveCursorStart => data.cursor_position = 0,
                        TextChange::MoveCursorEnd => data.cursor_position = data.text.len(),
                        TextChange::DeleteLeft => {
                            if data.cursor_position > 0 && data.cursor_position <= data.text.len() {
                                data.cursor_position -= 1;
                                data.text.remove(data.cursor_position);
                            }
                        }
                        TextChange::DeleteRight => if data.cursor_position < data.text.len() {
                            data.text.remove(data.cursor_position);
                        }
                        TextChange::NewLine => if data.allow_new_line {
                            data.cursor_position = data.cursor_position.min(data.text.len());
                            data.text.insert(data.cursor_position, '\n');
                            data.cursor_position += 1;
                        }
                    }
                    data.cursor_position = data.cursor_position.min(data.text.len());
                }
                if let Some(notify) = notify {
                    context.messenger.write(notify, InputFieldMessage {
                        sender: context.id.to_owned(),
                        data: data.clone(),
                    });
                }
                drop(context.state.write(data));
            }
        });
    }
}

widget_component! {
    pub input_field_content(props, state, named_slots) [use_input_field] {
        unpack_named_slots!(named_slots => content);
        if let Some(content_props) = content.props_mut() {
            if let Ok(s) = state.read::<InputFieldProps>() {
                content_props.write(s.clone());
            };
            if let Ok(p) = props.read::<ButtonProps>() {
                content_props.write(p.clone());
            };
            if let Ok(p) = props.read::<ButtonSettingsProps>() {
                content_props.write(p.clone());
            };
        }

        content
    }
}

widget_component! {
    pub input_field(key, props, named_slots) {
        unpack_named_slots!(named_slots => content);

        widget! {
            (#{key} button: {props.clone()} {
                content = (input_field_content: {props.clone()} {
                    content = {content}
                })
            })
        }
    }
}