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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#[cfg(feature = "native")]
use ambient_sys::time::SystemTime;
use std::time::Duration;
#[cfg(feature = "guest")]
use std::time::SystemTime;

use ambient_cb::{cb, Cb};
use ambient_element::{Element, ElementComponent, ElementComponentExt, Hooks};
use ambient_guest_bridge::core::{
    layout::components::space_between_items, rendering::components::color,
};
use ambient_time::parse_duration;
use glam::vec4;

use crate::{layout::FlowRow, text::Text};

use super::{ChangeCb, Editor, EditorOpts, TextEditor};

#[derive(
    Debug,
    Default,
    Clone,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    serde::Serialize,
    serde::Deserialize,
)]
/// A duration that can be edited.
pub struct EditableDuration {
    dur: Duration,
    valid: bool,
    input: String,
}

impl EditableDuration {
    /// Create a new [EditableDuration].
    pub fn new(dur: Duration, valid: bool, input: String) -> Self {
        Self { dur, valid, input }
    }

    /// Get the duration.
    pub fn dur(&self) -> Duration {
        self.dur
    }
}

impl From<Duration> for EditableDuration {
    fn from(v: Duration) -> Self {
        Self {
            dur: v,
            input: format!("{}s", v.as_secs()),
            valid: true,
        }
    }
}

impl From<EditableDuration> for Duration {
    fn from(v: EditableDuration) -> Self {
        v.dur
    }
}

impl From<&EditableDuration> for Duration {
    fn from(v: &EditableDuration) -> Self {
        v.dur
    }
}

impl From<String> for EditableDuration {
    fn from(s: String) -> Self {
        let dur = parse_duration(&s);
        let valid = dur.is_ok();
        Self {
            dur: dur.unwrap_or_default(),
            valid,
            input: s,
        }
    }
}

#[derive(Debug, Clone)]
/// An editor for [EditableDuration].
pub struct DurationEditor {
    /// The value to edit.
    pub value: EditableDuration,
    /// Callback for when the value changes.
    pub on_change: Cb<dyn Fn(EditableDuration) + Sync + Send>,
}

impl DurationEditor {
    /// Create a new [DurationEditor].
    pub fn new(
        value: EditableDuration,
        on_change: Cb<dyn Fn(EditableDuration) + Sync + Send>,
    ) -> Self {
        Self { value, on_change }
    }
}

impl ElementComponent for DurationEditor {
    fn render(self: Box<Self>, _: &mut Hooks) -> Element {
        let Self {
            value: EditableDuration { input, dur, valid },
            on_change,
        } = *self;
        let input = TextEditor::new(
            input,
            cb(move |upd: String| on_change(EditableDuration::from(upd))),
        )
        .el();
        let value = Text::el(format!("{dur:#?}"));

        if valid {
            FlowRow(vec![input, value])
                .el()
                .with(space_between_items(), 10.0)
        } else {
            FlowRow(vec![
                input,
                Text::el("invalid duration").with(color(), vec4(1.0, 0.0, 0.0, 1.0)),
            ])
            .el()
            .with(space_between_items(), 10.0)
        }
    }
}

impl Editor for EditableDuration {
    fn editor(self, on_change: ChangeCb<Self>, _: EditorOpts) -> Element {
        DurationEditor::new(self, on_change).el()
    }
}

#[derive(Debug, Clone)]
/// An editor for [SystemTime].
///
/// At present, this does not support editing and is purely display-only.
pub struct SystemTimeEditor {
    /// The value to edit.
    pub value: SystemTime,
    /// Callback for when the value changes.
    pub on_change: Option<Cb<dyn Fn(SystemTime) + Sync + Send>>,
}
impl ElementComponent for SystemTimeEditor {
    fn render(self: Box<Self>, _: &mut Hooks) -> Element {
        Text::el(format!("{:?}", self.value))
    }
}
impl Editor for SystemTime {
    fn editor(self, _: ChangeCb<Self>, _: EditorOpts) -> Element {
        SystemTimeEditor {
            value: self,
            on_change: None,
        }
        .el()
    }
}