windjammer_ui/components/generated/
propertyeditor.rs1#![allow(clippy::all)]
2#![allow(noop_method_call)]
3use super::traits::Renderable;
4
5#[derive(Clone, Debug, PartialEq)]
6pub enum PropertyType {
7 Number { min: f32, max: f32, step: f32 },
8 Integer { min: i32, max: i32 },
9 Boolean,
10 Text,
11 Color,
12 Dropdown { options: Vec<String> },
13}
14
15#[derive(Debug, Clone, PartialEq)]
16pub struct Property {
17 pub name: String,
18 pub value: String,
19 pub property_type: PropertyType,
20 pub unit: String,
21 pub tooltip: String,
22 pub on_change: String,
23}
24
25impl Property {
26 #[inline]
27 pub fn number(name: String, value: f32, min: f32, max: f32) -> Property {
28 Property {
29 name,
30 value: format!("{:.3}", value),
31 property_type: PropertyType::Number {
32 min,
33 max,
34 step: 0.1,
35 },
36 unit: "".to_string(),
37 tooltip: "".to_string(),
38 on_change: "".to_string(),
39 }
40 }
41 #[inline]
42 pub fn integer(name: String, value: i32, min: i32, max: i32) -> Property {
43 Property {
44 name,
45 value: format!("{}", value),
46 property_type: PropertyType::Integer { min, max },
47 unit: "".to_string(),
48 tooltip: "".to_string(),
49 on_change: "".to_string(),
50 }
51 }
52 #[inline]
53 pub fn boolean(name: String, value: bool) -> Property {
54 Property {
55 name,
56 value: {
57 if value {
58 "true".to_string()
59 } else {
60 "false".to_string()
61 }
62 },
63 property_type: PropertyType::Boolean,
64 unit: "".to_string(),
65 tooltip: "".to_string(),
66 on_change: "".to_string(),
67 }
68 }
69 #[inline]
70 pub fn text(name: String, value: String) -> Property {
71 Property {
72 name,
73 value,
74 property_type: PropertyType::Text,
75 unit: "".to_string(),
76 tooltip: "".to_string(),
77 on_change: "".to_string(),
78 }
79 }
80 #[inline]
81 pub fn color(name: String, value: String) -> Property {
82 Property {
83 name,
84 value,
85 property_type: PropertyType::Color,
86 unit: "".to_string(),
87 tooltip: "".to_string(),
88 on_change: "".to_string(),
89 }
90 }
91 #[inline]
92 pub fn unit(mut self, unit: String) -> Property {
93 self.unit = unit;
94 self
95 }
96 #[inline]
97 pub fn tooltip(mut self, tooltip: String) -> Property {
98 self.tooltip = tooltip;
99 self
100 }
101 #[inline]
102 pub fn on_change(mut self, handler: String) -> Property {
103 self.on_change = handler;
104 self
105 }
106}
107
108impl Renderable for Property {
109 #[inline]
110 fn render(self) -> String {
111 let tooltip_attr = {
112 if self.tooltip != "" {
113 format!(" title='{}'", self.tooltip)
114 } else {
115 "".to_string()
116 }
117 };
118 let unit_html = {
119 if self.unit != "" {
120 format!("<span class='prop-unit'>{}</span>", self.unit)
121 } else {
122 "".to_string()
123 }
124 };
125 let input_html = match self.property_type.clone() {
126 PropertyType::Number {
127 min: mn,
128 max: mx,
129 step: st,
130 } => {
131 format!(
132 "
133 <div class='prop-number'>
134 <input type='number' class='prop-input'
135 value='{}' min='{}' max='{}' step='{}'
136 onchange='{}(this.value)'/>
137 {}
138 </div>
139 ",
140 self.value, mn, mx, st, self.on_change, unit_html
141 )
142 }
143 PropertyType::Integer { min: mn, max: mx } => {
144 format!(
145 "
146 <div class='prop-number'>
147 <input type='number' class='prop-input'
148 value='{}' min='{}' max='{}' step='1'
149 onchange='{}(this.value)'/>
150 {}
151 </div>
152 ",
153 self.value, mn, mx, self.on_change, unit_html
154 )
155 }
156 PropertyType::Boolean => {
157 let checked = {
158 if self.value == "true" {
159 "checked".to_string()
160 } else {
161 "".to_string()
162 }
163 };
164 format!(
165 "
166 <label class='prop-toggle'>
167 <input type='checkbox' {} onchange='{}(this.checked)'/>
168 <span class='toggle-slider'></span>
169 </label>
170 ",
171 checked, self.on_change
172 )
173 }
174 PropertyType::Text => {
175 format!(
176 "
177 <input type='text' class='prop-input prop-text'
178 value='{}' onchange='{}(this.value)'/>
179 ",
180 self.value, self.on_change
181 )
182 }
183 PropertyType::Color => {
184 format!(
185 "
186 <div class='prop-color'>
187 <input type='color' class='color-swatch'
188 value='{}' onchange='{}(this.value)'/>
189 <input type='text' class='color-hex'
190 value='{}' onchange='{}(this.value)'/>
191 </div>
192 ",
193 self.value, self.on_change, self.value, self.on_change
194 )
195 }
196 PropertyType::Dropdown { options: opts } => {
197 let mut options_html = "".to_string();
198 for o in opts {
199 let selected = {
200 if o.as_str() == self.value.as_str() {
201 "selected".to_string()
202 } else {
203 "".to_string()
204 }
205 };
206 options_html +=
207 format!("<option value='{}' {}>{}</option>", o, selected, o).as_str();
208 }
209 format!(
210 "
211 <select class='prop-select' onchange='{}(this.value)'>
212 {}
213 </select>
214 ",
215 self.on_change, options_html
216 )
217 }
218 };
219 format!(
220 "
221 <div class='prop-row'{}>
222 <label class='prop-label'>{}</label>
223 <div class='prop-value'>
224 {}
225 </div>
226 </div>
227 ",
228 tooltip_attr, self.name, input_html
229 )
230 }
231}
232
233#[derive(Debug, Clone, PartialEq)]
234pub struct Vec3Editor {
235 pub label: String,
236 pub x: f32,
237 pub y: f32,
238 pub z: f32,
239 pub on_change: String,
240}
241
242impl Vec3Editor {
243 #[inline]
244 pub fn new(label: String, x: f32, y: f32, z: f32) -> Vec3Editor {
245 Vec3Editor {
246 label,
247 x,
248 y,
249 z,
250 on_change: "".to_string(),
251 }
252 }
253 #[inline]
254 pub fn on_change(mut self, handler: String) -> Vec3Editor {
255 self.on_change = handler;
256 self
257 }
258}
259
260impl Renderable for Vec3Editor {
261 #[inline]
262 fn render(self) -> String {
263 format!(
264 "
265 <div class='vec3-editor'>
266 <label class='prop-label'>{}</label>
267 <div class='vec3-inputs'>
268 <div class='vec3-axis'>
269 <span class='axis-label x'>X</span>
270 <input type='number' step='0.1' value='{:.3}'
271 onchange='{}(\"x\", this.value)'/>
272 </div>
273 <div class='vec3-axis'>
274 <span class='axis-label y'>Y</span>
275 <input type='number' step='0.1' value='{:.3}'
276 onchange='{}(\"y\", this.value)'/>
277 </div>
278 <div class='vec3-axis'>
279 <span class='axis-label z'>Z</span>
280 <input type='number' step='0.1' value='{:.3}'
281 onchange='{}(\"z\", this.value)'/>
282 </div>
283 </div>
284 </div>
285 ",
286 self.label, self.x, self.on_change, self.y, self.on_change, self.z, self.on_change
287 )
288 }
289}
290
291#[inline]
292pub fn property_editor_styles() -> String {
293 "
294 .prop-row {
295 display: flex;
296 align-items: center;
297 padding: 6px 0;
298 border-bottom: 1px solid rgba(255,255,255,0.05);
299 }
300
301 .prop-row:hover {
302 background: rgba(255,255,255,0.02);
303 }
304
305 .prop-label {
306 width: 100px;
307 font-size: 12px;
308 color: #999;
309 flex-shrink: 0;
310 }
311
312 .prop-value {
313 flex: 1;
314 }
315
316 .prop-input {
317 width: 100%;
318 padding: 6px 10px;
319 border: 1px solid #333;
320 border-radius: 4px;
321 background: #1a1a2e;
322 color: #e0e0e0;
323 font-size: 12px;
324 }
325
326 .prop-input:focus {
327 border-color: #e94560;
328 outline: none;
329 box-shadow: 0 0 0 2px rgba(233, 69, 96, 0.2);
330 }
331
332 .prop-number {
333 display: flex;
334 align-items: center;
335 gap: 4px;
336 }
337
338 .prop-unit {
339 font-size: 11px;
340 color: #666;
341 }
342
343 /* Toggle switch */
344 .prop-toggle {
345 position: relative;
346 display: inline-block;
347 width: 44px;
348 height: 24px;
349 }
350
351 .prop-toggle input {
352 opacity: 0;
353 width: 0;
354 height: 0;
355 }
356
357 .toggle-slider {
358 position: absolute;
359 cursor: pointer;
360 top: 0;
361 left: 0;
362 right: 0;
363 bottom: 0;
364 background-color: #333;
365 transition: 0.3s;
366 border-radius: 24px;
367 }
368
369 .toggle-slider:before {
370 position: absolute;
371 content: '';
372 height: 18px;
373 width: 18px;
374 left: 3px;
375 bottom: 3px;
376 background-color: white;
377 transition: 0.3s;
378 border-radius: 50%;
379 }
380
381 .prop-toggle input:checked + .toggle-slider {
382 background-color: #e94560;
383 }
384
385 .prop-toggle input:checked + .toggle-slider:before {
386 transform: translateX(20px);
387 }
388
389 /* Color editor */
390 .prop-color {
391 display: flex;
392 gap: 8px;
393 align-items: center;
394 }
395
396 .color-swatch {
397 width: 32px;
398 height: 32px;
399 border: none;
400 border-radius: 4px;
401 cursor: pointer;
402 }
403
404 .color-hex {
405 width: 80px;
406 padding: 6px 8px;
407 border: 1px solid #333;
408 border-radius: 4px;
409 background: #1a1a2e;
410 color: #e0e0e0;
411 font-family: monospace;
412 font-size: 12px;
413 }
414
415 /* Vec3 editor */
416 .vec3-editor {
417 display: flex;
418 align-items: center;
419 padding: 6px 0;
420 }
421
422 .vec3-inputs {
423 display: flex;
424 gap: 4px;
425 flex: 1;
426 }
427
428 .vec3-axis {
429 display: flex;
430 align-items: center;
431 flex: 1;
432 }
433
434 .vec3-axis input {
435 width: 100%;
436 padding: 6px 8px;
437 border: 1px solid #333;
438 border-radius: 0 4px 4px 0;
439 background: #1a1a2e;
440 color: #e0e0e0;
441 font-size: 12px;
442 }
443
444 .axis-label {
445 padding: 6px 8px;
446 font-size: 11px;
447 font-weight: 600;
448 border-radius: 4px 0 0 4px;
449 }
450
451 .axis-label.x { background: #e94560; color: white; }
452 .axis-label.y { background: #4ade80; color: #1a1a2e; }
453 .axis-label.z { background: #60a5fa; color: white; }
454
455 /* Select dropdown */
456 .prop-select {
457 width: 100%;
458 padding: 6px 10px;
459 border: 1px solid #333;
460 border-radius: 4px;
461 background: #1a1a2e;
462 color: #e0e0e0;
463 font-size: 12px;
464 cursor: pointer;
465 }
466 "
467 .to_string()
468}