textbox_rs/
textbox-rs.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2025 Fundament Research Institute <https://fundament.institute>
3
4use feather_ui::color::sRGB;
5use feather_ui::layout::{fixed, leaf};
6use feather_ui::text::{EditBuffer, EditView};
7use feather_ui::{DAbsRect, ScopeID, gen_id};
8
9use feather_ui::component::region::Region;
10use feather_ui::component::textbox;
11use feather_ui::component::textbox::TextBox;
12use feather_ui::component::window::Window;
13use feather_ui::layout::base;
14use feather_ui::persist::{FnPersist2, FnPersistStore};
15use feather_ui::{AbsRect, App, DRect, FILL_DRECT, RelRect, SourceID, cosmic_text};
16use std::sync::Arc;
17
18#[derive(PartialEq, Clone, Debug, Default)]
19struct TextState {
20    text: EditView,
21}
22
23struct BasicApp {}
24
25#[derive(Default, Clone, feather_macro::Empty, feather_macro::Area)]
26struct MinimalArea {
27    area: DRect,
28}
29
30impl base::ZIndex for MinimalArea {}
31impl base::Anchor for MinimalArea {}
32impl base::Limits for MinimalArea {}
33impl fixed::Prop for MinimalArea {}
34
35#[derive(
36    Clone,
37    feather_macro::Empty,
38    feather_macro::Area,
39    feather_macro::TextEdit,
40    feather_macro::Padding,
41)]
42struct MinimalText {
43    area: DRect,
44    padding: DAbsRect,
45    textedit: EditView,
46}
47impl base::Direction for MinimalText {}
48impl base::ZIndex for MinimalText {}
49impl base::Limits for MinimalText {}
50impl base::RLimits for MinimalText {}
51impl base::Anchor for MinimalText {}
52impl leaf::Padded for MinimalText {}
53impl leaf::Prop for MinimalText {}
54impl fixed::Child for MinimalText {}
55impl textbox::Prop for MinimalText {}
56
57impl FnPersistStore for BasicApp {
58    type Store = (TextState, im::HashMap<Arc<SourceID>, Option<Window>>);
59}
60
61impl FnPersist2<TextState, ScopeID<'_>, im::HashMap<Arc<SourceID>, Option<Window>>> for BasicApp {
62    fn init(&self) -> Self::Store {
63        (
64            TextState {
65                ..Default::default()
66            },
67            im::HashMap::new(),
68        )
69    }
70    fn call(
71        &mut self,
72        mut store: Self::Store,
73        args: TextState,
74        mut scope: ScopeID<'_>,
75    ) -> (Self::Store, im::HashMap<Arc<SourceID>, Option<Window>>) {
76        if store.0 != args {
77            let textbox = TextBox::new(
78                gen_id!(scope),
79                MinimalText {
80                    area: FILL_DRECT,
81                    padding: AbsRect::splat(12.0).into(),
82                    textedit: args.text.clone(), // Be careful to take the value from args, not store.0, which is stale.
83                },
84                40.0,
85                56.0,
86                cosmic_text::FamilyOwned::SansSerif,
87                sRGB::white(),
88                Default::default(),
89                Default::default(),
90                cosmic_text::Wrap::Word,
91                Some(cosmic_text::Align::Right),
92            );
93
94            let region = Region::new(
95                gen_id!(scope),
96                MinimalArea {
97                    area: AbsRect::new(90.0, 0.0, -90.0, -180.0) + RelRect::new(0.0, 0.0, 1.0, 1.0),
98                },
99                feather_ui::children![fixed::Prop, textbox],
100            );
101            let window = Window::new(
102                gen_id!(scope),
103                winit::window::Window::default_attributes()
104                    .with_title(env!("CARGO_CRATE_NAME"))
105                    .with_inner_size(winit::dpi::PhysicalSize::new(600, 400))
106                    .with_resizable(true),
107                Box::new(region),
108            );
109
110            store.1 = im::HashMap::new();
111            store.1.insert(window.id.clone(), Some(window));
112            store.0 = args.clone();
113        }
114        let windows = store.1.clone();
115        (store, windows)
116    }
117}
118
119fn main() {
120    let (mut app, event_loop, _, _) = App::<TextState, BasicApp>::new::<()>(
121        TextState {
122            text: EditBuffer::new("new text", (0, 0)).into(),
123        },
124        vec![],
125        BasicApp {},
126        |_| (),
127    )
128    .unwrap();
129
130    event_loop.run_app(&mut app).unwrap();
131}