1use core::f32;
5use feather_macro::*;
6use feather_ui::color::sRGB;
7use feather_ui::component::button::Button;
8use feather_ui::component::gridbox::GridBox;
9use feather_ui::component::region::Region;
10use feather_ui::component::shape::{Shape, ShapeKind};
11use feather_ui::component::text::Text;
12use feather_ui::component::window::Window;
13use feather_ui::component::{ChildOf, mouse_area};
14use feather_ui::layout::{base, fixed, grid, leaf};
15use feather_ui::persist::{FnPersist2, FnPersistStore};
16use feather_ui::{
17 AbsPoint, AbsRect, App, DAbsPoint, DAbsRect, DRect, DValue, FILL_DRECT, InputResult, RelRect,
18 ScopeID, Slot, SourceID, UNSIZED_AXIS,
19};
20use std::sync::Arc;
21
22#[derive(PartialEq, Clone, Debug)]
23struct CounterState {
24 count: usize,
25}
26
27#[derive(Default, Empty, Area, Anchor, ZIndex)]
28struct FixedData {
29 area: DRect,
30 anchor: feather_ui::DPoint,
31 zindex: i32,
32}
33
34impl base::Padding for FixedData {}
35impl base::Limits for FixedData {}
36impl base::RLimits for FixedData {}
37impl fixed::Prop for FixedData {}
38impl fixed::Child for FixedData {}
39impl leaf::Prop for FixedData {}
40impl leaf::Padded for FixedData {}
41
42#[derive(Default, Empty, Area, Direction, RLimits, Padding)]
43struct GridData {
44 area: DRect,
45 direction: feather_ui::RowDirection,
46 rlimits: feather_ui::RelLimits,
47 rows: Vec<DValue>,
48 columns: Vec<DValue>,
49 spacing: feather_ui::DPoint,
50 padding: DAbsRect,
51}
52
53impl base::Anchor for GridData {}
54impl base::Limits for GridData {}
55impl fixed::Child for GridData {}
56
57impl grid::Prop for GridData {
58 fn rows(&self) -> &[DValue] {
59 &self.rows
60 }
61
62 fn columns(&self) -> &[DValue] {
63 &self.columns
64 }
65
66 fn spacing(&self) -> feather_ui::DPoint {
67 self.spacing
68 }
69}
70
71#[derive(Default, Empty, Area)]
72struct GridChild {
73 area: DRect,
74 x: usize,
75 y: usize,
76}
77
78impl base::Padding for GridChild {}
79impl base::Anchor for GridChild {}
80impl base::Limits for GridChild {}
81impl base::Margin for GridChild {}
82impl base::RLimits for GridChild {}
83impl base::Order for GridChild {}
84impl leaf::Prop for GridChild {}
85impl leaf::Padded for GridChild {}
86
87impl grid::Child for GridChild {
88 fn coord(&self) -> (usize, usize) {
89 (self.y, self.x)
90 }
91
92 fn span(&self) -> (usize, usize) {
93 (1, 1)
94 }
95}
96
97struct BasicApp {}
98
99impl FnPersistStore for BasicApp {
100 type Store = (CounterState, im::HashMap<Arc<SourceID>, Option<Window>>);
101}
102
103impl FnPersist2<CounterState, ScopeID<'_>, im::HashMap<Arc<SourceID>, Option<Window>>>
104 for BasicApp
105{
106 fn init(&self) -> Self::Store {
107 (CounterState { count: 99999999 }, im::HashMap::new())
108 }
109 fn call(
110 &mut self,
111 mut store: Self::Store,
112 args: CounterState,
113 mut scope: ScopeID<'_>,
114 ) -> (Self::Store, im::HashMap<Arc<SourceID>, Option<Window>>) {
115 if store.0 != args {
116 let button = {
117 let text = {
118 Text::<FixedData> {
119 id: scope.create(),
120 props: FixedData {
121 area: AbsRect::new(10.0, 15.0, 10.0, 15.0)
122 + RelRect::new(0.0, 0.0, UNSIZED_AXIS, UNSIZED_AXIS),
123 anchor: feather_ui::RelPoint::zero().into(),
124 ..Default::default()
125 }
126 .into(),
127 text: format!("Boxes: {}", args.count),
128 font_size: 40.0,
129 line_height: 56.0,
130 ..Default::default()
131 }
132 };
133
134 let rect = Shape::<DRect, { ShapeKind::RoundRect as u8 }>::new(
135 scope.create(),
136 feather_ui::FILL_DRECT,
137 0.0,
138 0.0,
139 wide::f32x4::splat(10.0),
140 sRGB::new(0.2, 0.7, 0.4, 1.0),
141 sRGB::transparent(),
142 DAbsPoint::zero(),
143 );
144
145 Button::<FixedData>::new(
146 scope.create(),
147 FixedData {
148 area: AbsRect::new(0.0, 20.0, 0.0, 0.0)
149 + RelRect::new(0.5, 0.0, UNSIZED_AXIS, UNSIZED_AXIS),
150 anchor: feather_ui::RelPoint::new(0.5, 0.0).into(),
151 zindex: 0,
152 },
153 Slot(feather_ui::APP_SOURCE_ID.into(), 0),
154 feather_ui::children![fixed::Prop, rect, text],
155 )
156 };
157
158 const NUM_COLUMNS: usize = 5;
159 let rectgrid = {
160 let mut children: im::Vector<Option<Box<ChildOf<dyn grid::Prop>>>> =
161 im::Vector::new();
162 {
163 for (i, id) in scope.iter(0..args.count) {
164 children.push_back(Some(Box::new(Shape::<
165 GridChild,
166 { ShapeKind::RoundRect as u8 },
167 >::new(
168 id,
169 GridChild {
170 area: FILL_DRECT,
171 x: i % NUM_COLUMNS,
172 y: i / NUM_COLUMNS,
173 },
174 0.0,
175 0.0,
176 wide::f32x4::splat(4.0),
177 sRGB::new(
178 (0.1 * i as f32) % 1.0,
179 (0.65 * i as f32) % 1.0,
180 (0.2 * i as f32) % 1.0,
181 1.0,
182 ),
183 sRGB::transparent(),
184 DAbsPoint::zero(),
185 ))));
186 }
187 }
188
189 GridBox::<GridData>::new(
190 scope.create(),
191 GridData {
192 area: AbsRect::new(0.0, 200.0, 0.0, 0.0)
193 + RelRect::new(0.0, 0.0, UNSIZED_AXIS, 1.0),
194
195 rlimits: feather_ui::RelLimits::new(0.0..1.0, 0.0..),
196 direction: feather_ui::RowDirection::LeftToRight,
197 rows: [40.0, 20.0, 40.0, 20.0, 40.0, 20.0, 10.0]
198 .map(DValue::from)
199 .to_vec(),
200 columns: [80.0, 40.0, 80.0, 40.0, 80.0].map(DValue::from).to_vec(),
201 spacing: AbsPoint::new(4.0, 4.0).into(),
202 padding: AbsRect::new(8.0, 8.0, 8.0, 8.0).into(),
203 },
204 children,
205 )
206 };
207
208 let region = Region::new(
209 scope.create(),
210 FixedData {
211 area: FILL_DRECT,
212 zindex: 0,
213 ..Default::default()
214 },
215 feather_ui::children![fixed::Prop, button, rectgrid],
216 );
217 let window = Window::new(
218 scope.create(),
219 winit::window::Window::default_attributes()
220 .with_title(env!("CARGO_CRATE_NAME"))
221 .with_resizable(true),
222 Box::new(region),
223 );
224
225 store.1 = im::HashMap::new();
226 store.1.insert(window.id.clone(), Some(window));
227 store.0 = args.clone();
228 }
229 let windows = store.1.clone();
230 (store, windows)
231 }
232}
233
234use feather_ui::WrapEventEx;
235
236fn main() {
237 let onclick = Box::new(
238 |_: mouse_area::MouseAreaEvent,
239 mut appdata: feather_ui::AccessCell<CounterState>|
240 -> InputResult<()> {
241 {
242 appdata.count += 1;
243 InputResult::Consume(())
244 }
245 }
246 .wrap(),
247 );
248
249 let (mut app, event_loop, _, _) = App::<CounterState, BasicApp>::new::<()>(
250 CounterState { count: 0 },
251 vec![onclick],
252 BasicApp {},
253 |_| (),
254 )
255 .unwrap();
256
257 event_loop.run_app(&mut app).unwrap();
258}