1use anyhow::Context;
4use eframe::{emath::Rect, epaint::Stroke, epaint::textures::TextureOptions};
5use egui::{
6 Color32, FontData, FontDefinitions, FontId, Galley, PointerButton, Pos2, TextureHandle, Ui,
7 Vec2, text::CCursor,
8};
9use json::JsonValue;
10use kira::{
11 AudioManager, AudioManagerSettings, DefaultBackend, sound::static_sound::StaticSoundData,
12};
13use std::{
14 any::Any,
15 collections::HashMap,
16 error::Error,
17 fmt::Debug,
18 fs::{self, File},
19 io::Read,
20 path::{Path, PathBuf},
21 sync::Arc,
22 time::Instant,
23 vec::Vec,
24};
25
26pub fn create_json<P: AsRef<Path>>(path: P, data: JsonValue) -> anyhow::Result<()> {
28 let parent_dir = path
29 .as_ref()
30 .parent()
31 .ok_or_else(|| anyhow::anyhow!("Invalid file path."))?;
32
33 fs::create_dir_all(parent_dir)?;
35
36 let formatted = json::stringify_pretty(data, 4);
38
39 fs::write(path, formatted)?;
41 Ok(())
42}
43
44pub fn copy_and_reformat_json<P: AsRef<Path>>(src: P, dest: P) -> anyhow::Result<()> {
46 let content = fs::read_to_string(&src)?;
48
49 let parsed = json::parse(&content)?;
51
52 create_json(dest, parsed)?;
54
55 Ok(())
56}
57
58pub fn check_file_exists<P: AsRef<Path>>(path: P) -> bool {
60 let path_ref = path.as_ref();
61 if path_ref.exists() {
62 true } else {
64 false
66 }
67}
68
69pub fn write_to_json<P: AsRef<Path>>(path: P, data: JsonValue) -> anyhow::Result<()> {
71 let parent_dir = path
72 .as_ref()
73 .parent()
74 .ok_or_else(|| anyhow::anyhow!("Invalid file path."))?;
75
76 fs::create_dir_all(parent_dir)?;
77 let formatted = json::stringify_pretty(data, 4);
78 fs::write(path, formatted)?;
79 Ok(())
80}
81
82pub fn read_from_json<P: AsRef<Path>>(path: P) -> anyhow::Result<JsonValue> {
84 let content = fs::read_to_string(&path)
85 .with_context(|| format!("Cannot read the file: {}", path.as_ref().display()))?;
86 json::parse(&content)
87 .with_context(|| format!("Failed to parse JSON: {}", path.as_ref().display()))
88}
89
90pub fn play_wav(path: &str) -> anyhow::Result<f64> {
92 let mut manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())?;
93 let sound_data = StaticSoundData::from_file(path)?;
94 let duration = sound_data.duration().as_secs_f64();
95 manager.play(sound_data)?;
96 std::thread::sleep(std::time::Duration::from_secs_f64(duration));
97 Ok(duration)
98}
99
100pub fn general_click_feedback(sound_path: &str) {
102 let sound_path = sound_path.to_string();
103 std::thread::spawn(move || {
104 play_wav(&sound_path).unwrap_or(0_f64);
105 });
106}
107
108pub fn count_files_recursive(dir: &Path, target: &str) -> std::io::Result<usize> {
110 let mut count = 0;
111 if dir.is_dir() {
112 for entry in fs::read_dir(dir)? {
113 let entry = entry?;
114 let path = entry.path();
115 if path.is_dir() {
116 count += count_files_recursive(&path, target)?;
117 } else if path.file_name().unwrap().to_string_lossy().contains(target) {
118 count += 1;
119 }
120 }
121 }
122 Ok(count)
123}
124
125pub fn list_files_recursive(path: &Path, prefix: &str) -> Result<Vec<PathBuf>, std::io::Error> {
127 let mut matches = Vec::new();
128
129 for entry in std::fs::read_dir(path)? {
130 let entry = entry?;
132 let path = entry.path();
133
134 if path.is_dir() {
135 matches.extend(list_files_recursive(&path, prefix)?);
137 } else if let Some(file_name) = path.file_name()
138 && file_name.to_string_lossy().contains(prefix)
139 {
140 matches.push(path);
141 }
142 }
143
144 Ok(matches)
145}
146
147#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
149pub struct Config {
150 pub strict_mode: bool,
152 pub problem_report_sound: String,
154}
155
156impl Default for Config {
157 fn default() -> Self {
158 Self {
159 strict_mode: true,
160 problem_report_sound: String::new(),
161 }
162 }
163}
164
165impl Config {
166 #[inline]
167 pub fn strict_mode(mut self, strict_mode: bool) -> Self {
168 self.strict_mode = strict_mode;
169 self
170 }
171
172 #[inline]
173 pub fn problem_report_sound(mut self, problem_report_sound: &str) -> Self {
174 self.problem_report_sound = problem_report_sound.to_string();
175 self
176 }
177
178 pub fn from_json_value(value: &JsonValue) -> Option<Self> {
179 Some(Self {
180 strict_mode: value["strict_mode"].as_bool()?,
181 problem_report_sound: value["problem_report_sound"].as_str()?.to_string(),
182 })
183 }
184
185 pub fn to_json_value(&self) -> JsonValue {
186 json::object! {
187 strict_mode: self.strict_mode,
188 problem_report_sound: self.problem_report_sound.clone(),
189 }
190 }
191}
192
193#[derive(Debug, Clone, PartialEq, Eq, Default)]
195pub struct AppText {
196 pub app_text: HashMap<String, Vec<String>>,
197}
198
199impl AppText {
200 pub fn new(app_text: HashMap<String, Vec<String>>) -> Self {
201 Self { app_text }
202 }
203
204 pub fn from_json_value(value: &JsonValue) -> Option<Self> {
205 if !value["app_text"].is_object() {
207 return None;
208 }
209
210 let mut parsed = HashMap::new();
212 for (key, val) in value["app_text"].entries() {
213 if let JsonValue::Array(arr) = val {
214 let str_vec: Vec<String> = arr
215 .iter()
216 .filter_map(|v| v.as_str().map(String::from))
217 .collect();
218 parsed.insert(key.to_string(), str_vec);
219 }
220 }
221
222 Some(Self { app_text: parsed })
223 }
224}
225
226#[derive(Debug, Clone, PartialEq, PartialOrd)]
228pub enum Value {
229 Bool(bool),
230 Int(i32),
231 UInt(u32),
232 Float(f32),
233 Vec(Vec<Value>),
234 String(String),
235}
236
237impl From<bool> for Value {
238 fn from(b: bool) -> Self {
239 Value::Bool(b)
240 }
241}
242
243impl From<i32> for Value {
244 fn from(i: i32) -> Self {
245 Value::Int(i)
246 }
247}
248
249impl From<u32> for Value {
250 fn from(u: u32) -> Self {
251 Value::UInt(u)
252 }
253}
254
255impl From<f32> for Value {
256 fn from(f: f32) -> Self {
257 Value::Float(f)
258 }
259}
260
261impl<T: Into<Value>> From<Vec<T>> for Value {
262 fn from(v: Vec<T>) -> Self {
263 Value::Vec(v.into_iter().map(|x| x.into()).collect())
264 }
265}
266
267impl From<String> for Value {
268 fn from(s: String) -> Self {
269 Value::String(s)
270 }
271}
272
273#[derive(Debug, Clone, PartialEq, PartialOrd)]
275pub struct ReportState {
276 pub current_page: String,
278 pub current_total_runtime: f32,
280 pub current_page_runtime: f32,
282}
283
284#[derive(Debug, Clone, PartialEq, PartialOrd)]
286pub struct Problem {
287 pub severity_level: SeverityLevel,
289 pub problem: String,
291 pub annotation: String,
293 pub report_state: ReportState,
295 pub problem_type: RustConstructorError,
297}
298
299#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
301pub enum SeverityLevel {
302 MildWarning,
304 SevereWarning,
306 Error,
308}
309
310pub trait RustConstructorResource: Debug {
312 fn name(&self) -> &str;
314
315 fn expose_type(&self) -> &str;
317
318 fn reg_render_resource(&self, render_list: &mut Vec<RenderResource>) {
320 render_list.push(RenderResource {
321 discern_type: self.expose_type().to_string(),
322 name: self.name().to_string(),
323 });
324 }
325
326 fn as_any(&self) -> &dyn Any;
328
329 fn as_any_mut(&mut self) -> &mut dyn Any;
331}
332
333impl RustConstructorResource for PageData {
334 fn name(&self) -> &str {
335 &self.name
336 }
337
338 fn expose_type(&self) -> &str {
339 &self.discern_type
340 }
341
342 fn as_any(&self) -> &dyn Any {
343 self
344 }
345
346 fn as_any_mut(&mut self) -> &mut dyn Any {
347 self
348 }
349}
350
351#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
353pub struct PageData {
354 pub discern_type: String,
355 pub name: String,
356 pub forced_update: bool,
358 pub change_page_updated: bool,
360 pub enter_page_updated: bool,
362}
363
364impl Default for PageData {
365 fn default() -> Self {
366 PageData {
367 discern_type: String::from("PageData"),
368 name: String::from("PageData"),
369 forced_update: true,
370 change_page_updated: false,
371 enter_page_updated: false,
372 }
373 }
374}
375
376impl PageData {
377 #[inline]
378 pub fn name(mut self, name: &str) -> Self {
379 self.name = name.to_string();
380 self
381 }
382
383 #[inline]
384 pub fn forced_update(mut self, forced_update: bool) -> Self {
385 self.forced_update = forced_update;
386 self
387 }
388}
389
390#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
392pub struct Timer {
393 pub start_time: f32,
395 pub total_time: f32,
397 pub timer: Instant,
399 pub now_time: f32,
401}
402
403impl Default for Timer {
404 fn default() -> Self {
405 Timer {
406 start_time: 0_f32,
407 total_time: 0_f32,
408 timer: Instant::now(),
409 now_time: 0_f32,
410 }
411 }
412}
413
414impl RustConstructorResource for ImageTexture {
415 fn name(&self) -> &str {
416 &self.name
417 }
418
419 fn expose_type(&self) -> &str {
420 &self.discern_type
421 }
422
423 fn as_any(&self) -> &dyn Any {
424 self
425 }
426
427 fn as_any_mut(&mut self) -> &mut dyn Any {
428 self
429 }
430}
431
432#[derive(Clone, PartialEq, Eq, Hash)]
434pub struct DebugTextureHandle(TextureHandle);
435
436impl std::fmt::Debug for DebugTextureHandle {
437 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
438 f.debug_struct("DebugTextureHandle").finish()
440 }
441}
442
443impl DebugTextureHandle {
444 pub fn new(texture_handle: TextureHandle) -> Self {
445 Self(texture_handle)
446 }
447}
448
449#[derive(Debug, Clone, PartialEq, Eq, Hash)]
451pub struct ImageTexture {
452 pub discern_type: String,
453 pub name: String,
454 pub texture: Option<DebugTextureHandle>,
456 pub cite_path: String,
458}
459
460impl Default for ImageTexture {
461 fn default() -> Self {
462 ImageTexture {
463 discern_type: String::from("ImageTexture"),
464 name: String::from("ImageTexture"),
465 texture: None,
466 cite_path: String::from(""),
467 }
468 }
469}
470
471impl ImageTexture {
472 #[inline]
473 pub fn name(mut self, name: &str) -> Self {
474 self.name = name.to_string();
475 self
476 }
477}
478
479impl RustConstructorResource for CustomRect {
480 fn name(&self) -> &str {
481 &self.name
482 }
483
484 fn expose_type(&self) -> &str {
485 &self.discern_type
486 }
487
488 fn as_any(&self) -> &dyn Any {
489 self
490 }
491
492 fn as_any_mut(&mut self) -> &mut dyn Any {
493 self
494 }
495}
496
497#[derive(Debug, Clone, PartialEq, PartialOrd)]
499pub struct CustomRect {
500 pub discern_type: String,
501 pub name: String,
502 pub position: [f32; 2],
504 pub size: [f32; 2],
506 pub rounding: f32,
508 pub x_grid: [u32; 2],
510 pub y_grid: [u32; 2],
512 pub center_display: (HorizontalAlign, VerticalAlign),
514 pub color: [u8; 4],
516 pub border_width: f32,
518 pub border_color: [u8; 4],
520 pub origin_position: [f32; 2],
522}
523
524impl Default for CustomRect {
525 fn default() -> Self {
526 Self {
527 discern_type: String::from("CustomRect"),
528 name: String::from("CustomRect"),
529 position: [0_f32, 0_f32],
530 size: [100_f32, 100_f32],
531 rounding: 2_f32,
532 x_grid: [0, 0],
533 y_grid: [0, 0],
534 center_display: (HorizontalAlign::default(), VerticalAlign::default()),
535 color: [255, 255, 255, 255],
536 border_width: 2_f32,
537 border_color: [0, 0, 0, 255],
538 origin_position: [0_f32, 0_f32],
539 }
540 }
541}
542
543impl CustomRect {
544 #[inline]
545 pub fn name(mut self, name: &str) -> Self {
546 self.name = name.to_string();
547 self
548 }
549
550 #[inline]
551 pub fn size(mut self, width: f32, height: f32) -> Self {
552 self.size = [width, height];
553 self
554 }
555
556 #[inline]
557 pub fn rounding(mut self, rounding: f32) -> Self {
558 self.rounding = rounding;
559 self
560 }
561
562 #[inline]
563 pub fn x_grid(mut self, fetch: u32, total: u32) -> Self {
564 self.x_grid = [fetch, total];
565 self
566 }
567
568 #[inline]
569 pub fn y_grid(mut self, fetch: u32, total: u32) -> Self {
570 self.y_grid = [fetch, total];
571 self
572 }
573
574 #[inline]
575 pub fn center_display(
576 mut self,
577 horizontal_align: HorizontalAlign,
578 vertical_align: VerticalAlign,
579 ) -> Self {
580 self.center_display = (horizontal_align, vertical_align);
581 self
582 }
583
584 #[inline]
585 pub fn color(mut self, r: u8, g: u8, b: u8, a: u8) -> Self {
586 self.color = [r, g, b, a];
587 self
588 }
589
590 #[inline]
591 pub fn border_width(mut self, border_width: f32) -> Self {
592 self.border_width = border_width;
593 self
594 }
595
596 #[inline]
597 pub fn border_color(mut self, r: u8, g: u8, b: u8, a: u8) -> Self {
598 self.border_color = [r, g, b, a];
599 self
600 }
601
602 #[inline]
603 pub fn origin_position(mut self, x: f32, y: f32) -> Self {
604 self.origin_position = [x, y];
605 self
606 }
607}
608
609impl RustConstructorResource for Image {
610 fn name(&self) -> &str {
611 &self.name
612 }
613
614 fn expose_type(&self) -> &str {
615 &self.discern_type
616 }
617
618 fn as_any(&self) -> &dyn Any {
619 self
620 }
621
622 fn as_any_mut(&mut self) -> &mut dyn Any {
623 self
624 }
625}
626
627#[derive(Debug, Clone, PartialEq)]
629pub struct Image {
630 pub discern_type: String,
631 pub name: String,
632 pub texture: Option<DebugTextureHandle>,
634 pub position: [f32; 2],
636 pub size: [f32; 2],
638 pub x_grid: [u32; 2],
640 pub y_grid: [u32; 2],
642 pub center_display: (HorizontalAlign, VerticalAlign),
644 pub alpha: u8,
646 pub overlay_color: [u8; 4],
648 pub use_overlay_color: bool,
650 pub origin_position: [f32; 2],
652 pub cite_texture: String,
654 pub last_frame_cite_texture: String,
656}
657
658impl Default for Image {
659 fn default() -> Self {
660 Self {
661 discern_type: String::from("Image"),
662 name: String::from("Image"),
663 texture: None,
664 position: [0_f32, 0_f32],
665 size: [100_f32, 100_f32],
666 x_grid: [0, 0],
667 y_grid: [0, 0],
668 center_display: (HorizontalAlign::default(), VerticalAlign::default()),
669 alpha: 255,
670 overlay_color: [255, 255, 255, 255],
671 use_overlay_color: true,
672 origin_position: [0_f32, 0_f32],
673 cite_texture: String::from("ImageTexture"),
674 last_frame_cite_texture: String::from("ImageTexture"),
675 }
676 }
677}
678
679impl Image {
680 #[inline]
681 pub fn name(mut self, name: &str) -> Self {
682 self.name = name.to_string();
683 self
684 }
685
686 #[inline]
687 pub fn origin_position(mut self, x: f32, y: f32) -> Self {
688 self.origin_position = [x, y];
689 self
690 }
691
692 #[inline]
693 pub fn size(mut self, width: f32, height: f32) -> Self {
694 self.size = [width, height];
695 self
696 }
697
698 #[inline]
699 pub fn x_grid(mut self, fetch: u32, total: u32) -> Self {
700 self.x_grid = [fetch, total];
701 self
702 }
703
704 #[inline]
705 pub fn y_grid(mut self, fetch: u32, total: u32) -> Self {
706 self.y_grid = [fetch, total];
707 self
708 }
709
710 #[inline]
711 pub fn center_display(
712 mut self,
713 horizontal_align: HorizontalAlign,
714 vertical_align: VerticalAlign,
715 ) -> Self {
716 self.center_display = (horizontal_align, vertical_align);
717 self
718 }
719
720 #[inline]
721 pub fn alpha(mut self, alpha: u8) -> Self {
722 self.alpha = alpha;
723 self
724 }
725
726 #[inline]
727 pub fn overlay_color(mut self, r: u8, g: u8, b: u8, a: u8) -> Self {
728 self.overlay_color = [r, g, b, a];
729 self
730 }
731
732 #[inline]
733 pub fn use_overlay_color(mut self, use_overlay_color: bool) -> Self {
734 self.use_overlay_color = use_overlay_color;
735 self
736 }
737}
738
739impl RustConstructorResource for Text {
740 fn name(&self) -> &str {
741 &self.name
742 }
743
744 fn expose_type(&self) -> &str {
745 &self.discern_type
746 }
747
748 fn as_any(&self) -> &dyn Any {
749 self
750 }
751
752 fn as_any_mut(&mut self) -> &mut dyn Any {
753 self
754 }
755}
756
757#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
759pub enum HyperlinkSelectMethod {
760 All(String),
762 Segment(Vec<(usize, String)>),
764}
765
766#[derive(Debug, Clone, PartialEq, PartialOrd)]
768pub struct Text {
769 pub discern_type: String,
770 pub name: String,
771 pub content: String,
773 pub font_size: f32,
775 pub color: [u8; 4],
777 pub position: [f32; 2],
779 pub center_display: (HorizontalAlign, VerticalAlign),
781 pub wrap_width: f32,
783 pub write_background: bool,
785 pub background_color: [u8; 4],
787 pub background_rounding: f32,
789 pub x_grid: [u32; 2],
791 pub y_grid: [u32; 2],
793 pub origin_position: [f32; 2],
795 pub font: String,
797 pub selection: Option<(usize, usize)>,
799 pub selectable: bool,
801 pub hyperlink_text: Vec<(String, HyperlinkSelectMethod)>,
803 pub hyperlink_index: Vec<(usize, usize, String)>,
805 pub last_frame_content: String,
807}
808
809impl Default for Text {
810 fn default() -> Self {
811 Self {
812 discern_type: String::from("Text"),
813 name: String::from("Text"),
814 content: String::from("Hello world"),
815 font_size: 16_f32,
816 color: [255, 255, 255, 255],
817 position: [0_f32, 0_f32],
818 center_display: (HorizontalAlign::default(), VerticalAlign::default()),
819 wrap_width: 200_f32,
820 write_background: true,
821 background_color: [0, 0, 0, 255],
822 background_rounding: 2_f32,
823 x_grid: [0, 0],
824 y_grid: [0, 0],
825 origin_position: [0_f32, 0_f32],
826 font: String::new(),
827 selection: None,
828 selectable: true,
829 hyperlink_text: Vec::new(),
830 hyperlink_index: Vec::new(),
831 last_frame_content: String::from("Hello world"),
832 }
833 }
834}
835
836impl Text {
837 #[inline]
838 pub fn name(mut self, name: &str) -> Self {
839 self.name = name.to_string();
840 self
841 }
842
843 #[inline]
844 pub fn content(mut self, content: &str) -> Self {
845 self.content = content.to_string();
846 self
847 }
848
849 #[inline]
850 pub fn font_size(mut self, font_size: f32) -> Self {
851 self.font_size = font_size;
852 self
853 }
854
855 #[inline]
856 pub fn color(mut self, r: u8, g: u8, b: u8, a: u8) -> Self {
857 self.color = [r, g, b, a];
858 self
859 }
860
861 #[inline]
862 pub fn center_display(
863 mut self,
864 horizontal_align: HorizontalAlign,
865 vertical_align: VerticalAlign,
866 ) -> Self {
867 self.center_display = (horizontal_align, vertical_align);
868 self
869 }
870
871 #[inline]
872 pub fn wrap_width(mut self, wrap_width: f32) -> Self {
873 self.wrap_width = wrap_width;
874 self
875 }
876
877 #[inline]
878 pub fn write_background(mut self, write_background: bool) -> Self {
879 self.write_background = write_background;
880 self
881 }
882
883 #[inline]
884 pub fn background_color(mut self, r: u8, g: u8, b: u8, a: u8) -> Self {
885 self.background_color = [r, g, b, a];
886 self
887 }
888
889 #[inline]
890 pub fn background_rounding(mut self, background_rounding: f32) -> Self {
891 self.background_rounding = background_rounding;
892 self
893 }
894
895 #[inline]
896 pub fn x_grid(mut self, fetch: u32, total: u32) -> Self {
897 self.x_grid = [fetch, total];
898 self
899 }
900
901 #[inline]
902 pub fn y_grid(mut self, fetch: u32, total: u32) -> Self {
903 self.y_grid = [fetch, total];
904 self
905 }
906
907 #[inline]
908 pub fn origin_position(mut self, x: f32, y: f32) -> Self {
909 self.origin_position = [x, y];
910 self
911 }
912
913 #[inline]
914 pub fn font(mut self, font: &str) -> Self {
915 self.font = font.to_string();
916 self
917 }
918
919 #[inline]
920 pub fn selectable(mut self, selectable: bool) -> Self {
921 self.selectable = selectable;
922 self
923 }
924
925 #[inline]
926 pub fn hyperlink_text(
927 mut self,
928 target_text: &str,
929 select_method: HyperlinkSelectMethod,
930 ) -> Self {
931 self.hyperlink_text
932 .push((target_text.to_string(), select_method));
933 self
934 }
935}
936
937impl RustConstructorResource for Variable {
938 fn name(&self) -> &str {
939 &self.name
940 }
941
942 fn expose_type(&self) -> &str {
943 &self.discern_type
944 }
945
946 fn as_any(&self) -> &dyn Any {
947 self
948 }
949
950 fn as_any_mut(&mut self) -> &mut dyn Any {
951 self
952 }
953}
954
955#[derive(Debug, Clone, PartialEq, PartialOrd)]
957pub struct Variable {
958 pub discern_type: String,
959 pub name: String,
960 pub value: Value,
962}
963
964impl Default for Variable {
965 fn default() -> Self {
966 Variable {
967 discern_type: String::from("Variable"),
968 name: String::from("Variable"),
969 value: Value::String(String::from("Hello world")),
970 }
971 }
972}
973
974impl Variable {
975 #[inline]
976 pub fn name(mut self, name: &str) -> Self {
977 self.name = name.to_string();
978 self
979 }
980
981 #[inline]
982 pub fn value<T: Into<Value>>(mut self, value: T) -> Self {
983 self.value = value.into();
984 self
985 }
986
987 pub fn new<T: Into<Value>>(name: &str, value: T) -> Self {
988 Self {
989 discern_type: String::from("Variable"),
990 name: String::from(name),
991 value: value.into(),
992 }
993 }
994
995 pub fn from_bool(name: &str, value: bool) -> Self {
996 Self {
997 discern_type: String::from("Variable"),
998 name: String::from(name),
999 value: Value::Bool(value),
1000 }
1001 }
1002
1003 pub fn from_int(name: &str, value: i32) -> Self {
1004 Self {
1005 discern_type: String::from("Variable"),
1006 name: String::from(name),
1007 value: Value::Int(value),
1008 }
1009 }
1010
1011 pub fn from_uint(name: &str, value: u32) -> Self {
1012 Self {
1013 discern_type: String::from("Variable"),
1014 name: String::from(name),
1015 value: Value::UInt(value),
1016 }
1017 }
1018
1019 pub fn from_float(name: &str, value: f32) -> Self {
1020 Self {
1021 discern_type: String::from("Variable"),
1022 name: String::from(name),
1023 value: Value::Float(value),
1024 }
1025 }
1026
1027 pub fn from_vec(name: &str, value: Vec<Value>) -> Self {
1028 Self {
1029 discern_type: String::from("Variable"),
1030 name: String::from(name),
1031 value: Value::Vec(value),
1032 }
1033 }
1034
1035 pub fn from_string<T: Into<String>>(name: &str, value: T) -> Self {
1036 Self {
1037 discern_type: String::from("Variable"),
1038 name: String::from(name),
1039 value: Value::String(value.into()),
1040 }
1041 }
1042}
1043
1044#[derive(Debug, Clone, PartialEq)]
1046pub struct Font {
1047 pub name: String,
1048 pub discern_type: String,
1049 pub font_definitions: FontDefinitions,
1051 pub path: String,
1053}
1054
1055impl Default for Font {
1056 fn default() -> Self {
1057 Self {
1058 discern_type: String::from("Font"),
1059 name: String::from("Font"),
1060 font_definitions: FontDefinitions::default(),
1061 path: String::from(""),
1062 }
1063 }
1064}
1065
1066impl Font {
1067 #[inline]
1068 pub fn name(mut self, name: &str) -> Self {
1069 self.name = name.to_string();
1070 self
1071 }
1072
1073 #[inline]
1074 pub fn path(mut self, path: &str) -> Self {
1075 self.path = path.to_string();
1076 self
1077 }
1078}
1079
1080impl RustConstructorResource for Font {
1081 fn name(&self) -> &str {
1082 &self.name
1083 }
1084
1085 fn expose_type(&self) -> &str {
1086 &self.discern_type
1087 }
1088
1089 fn as_any(&self) -> &dyn Any {
1090 self
1091 }
1092
1093 fn as_any_mut(&mut self) -> &mut dyn Any {
1094 self
1095 }
1096}
1097
1098impl RustConstructorResource for SplitTime {
1099 fn name(&self) -> &str {
1100 &self.name
1101 }
1102
1103 fn expose_type(&self) -> &str {
1104 &self.discern_type
1105 }
1106
1107 fn as_any(&self) -> &dyn Any {
1108 self
1109 }
1110
1111 fn as_any_mut(&mut self) -> &mut dyn Any {
1112 self
1113 }
1114}
1115
1116#[derive(Debug, Clone, PartialEq, PartialOrd)]
1118pub struct SplitTime {
1119 pub discern_type: String,
1120 pub name: String,
1121 pub time: [f32; 2],
1123}
1124
1125impl Default for SplitTime {
1126 fn default() -> Self {
1127 Self {
1128 discern_type: String::from("SplitTime"),
1129 name: String::from("SplitTime"),
1130 time: [0_f32, 0_f32],
1131 }
1132 }
1133}
1134
1135impl SplitTime {
1136 #[inline]
1137 pub fn name(mut self, name: &str) -> Self {
1138 self.name = name.to_string();
1139 self
1140 }
1141}
1142
1143impl RustConstructorResource for Switch {
1144 fn name(&self) -> &str {
1145 &self.name
1146 }
1147
1148 fn expose_type(&self) -> &str {
1149 &self.discern_type
1150 }
1151
1152 fn as_any(&self) -> &dyn Any {
1153 self
1154 }
1155
1156 fn as_any_mut(&mut self) -> &mut dyn Any {
1157 self
1158 }
1159}
1160
1161#[derive(Debug, Clone, PartialEq)]
1163pub struct Switch {
1164 pub discern_type: String,
1165 pub name: String,
1166 pub appearance: Vec<SwitchData>,
1168 pub switch_image_name: String,
1170 pub enable_hover_click_image: [bool; 2],
1172 pub state: u32,
1174 pub click_method: Vec<SwitchClickAction>,
1176 pub last_time_hovered: bool,
1178 pub last_time_clicked: bool,
1180 pub last_time_clicked_index: usize,
1182 pub animation_count: u32,
1184 pub hint_text_name: String,
1186 pub text_name: String,
1188 pub text_origin_position: [f32; 2],
1190 pub sound_path: String,
1192}
1193
1194impl Default for Switch {
1195 fn default() -> Self {
1196 Self {
1197 discern_type: String::from("Switch"),
1198 name: String::from("Switch"),
1199 appearance: vec![],
1200 switch_image_name: String::from("Image"),
1201 enable_hover_click_image: [false, false],
1202 state: 0,
1203 click_method: vec![],
1204 last_time_hovered: false,
1205 last_time_clicked: false,
1206 last_time_clicked_index: 0,
1207 animation_count: 0,
1208 hint_text_name: String::from("HintText"),
1209 text_name: String::from("Text"),
1210 text_origin_position: [0_f32, 0_f32],
1211 sound_path: String::from(""),
1212 }
1213 }
1214}
1215
1216impl Switch {
1217 #[inline]
1218 pub fn name(mut self, name: &str) -> Self {
1219 self.name = name.to_string();
1220 self
1221 }
1222
1223 #[inline]
1224 pub fn appearance(mut self, appearance: Vec<SwitchData>) -> Self {
1225 self.appearance = appearance;
1226 self
1227 }
1228
1229 #[inline]
1230 pub fn enable_hover_click_image(
1231 mut self,
1232 enable_hover_image: bool,
1233 enable_click_image: bool,
1234 ) -> Self {
1235 self.enable_hover_click_image = [enable_hover_image, enable_click_image];
1236 self
1237 }
1238
1239 #[inline]
1240 pub fn click_method(mut self, click_method: Vec<SwitchClickAction>) -> Self {
1241 self.click_method = click_method;
1242 self
1243 }
1244
1245 #[inline]
1246 pub fn sound_path(mut self, sound_path: &str) -> Self {
1247 self.sound_path = sound_path.to_string();
1248 self
1249 }
1250}
1251
1252#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1254pub struct RenderResource {
1255 pub discern_type: String,
1256 pub name: String,
1257}
1258
1259#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1261pub struct SwitchData {
1262 pub texture: String,
1264 pub color: [u8; 4],
1266 pub text: String,
1268 pub hint_text: String,
1270}
1271
1272#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1274pub struct SwitchClickAction {
1275 pub click_method: PointerButton,
1277 pub action: bool,
1279}
1280
1281#[derive(Debug, Clone, PartialEq, PartialOrd)]
1283pub struct MessageBox {
1284 pub discern_type: String,
1285 pub name: String,
1286 pub size: [f32; 2],
1288 pub content_name: String,
1290 pub title_name: String,
1292 pub image_name: String,
1294 pub keep_existing: bool,
1296 pub existing_time: f32,
1298 pub exist: bool,
1300 pub speed: f32,
1302 pub restore_speed: f32,
1304 pub memory_offset: f32,
1306}
1307
1308impl Default for MessageBox {
1309 fn default() -> Self {
1310 Self {
1311 discern_type: String::from("MessageBox"),
1312 name: String::from("MessageBox"),
1313 size: [100_f32, 100_f32],
1314 content_name: String::from("Content"),
1315 title_name: String::from("Title"),
1316 image_name: String::from("Image"),
1317 keep_existing: false,
1318 existing_time: 3_f32,
1319 exist: true,
1320 speed: 30_f32,
1321 restore_speed: 10_f32,
1322 memory_offset: 0_f32,
1323 }
1324 }
1325}
1326
1327impl MessageBox {
1328 #[inline]
1329 pub fn name(mut self, name: &str) -> Self {
1330 self.name = name.to_string();
1331 self
1332 }
1333
1334 #[inline]
1335 pub fn size(mut self, width: f32, height: f32) -> Self {
1336 self.size = [width, height];
1337 self
1338 }
1339
1340 #[inline]
1341 pub fn keep_existing(mut self, keep_existing: bool) -> Self {
1342 self.keep_existing = keep_existing;
1343 self
1344 }
1345
1346 #[inline]
1347 pub fn existing_time(mut self, existing_time: f32) -> Self {
1348 self.existing_time = existing_time;
1349 self
1350 }
1351
1352 #[inline]
1353 pub fn speed(mut self, speed: f32) -> Self {
1354 self.speed = speed;
1355 self
1356 }
1357
1358 #[inline]
1359 pub fn restore_speed(mut self, restore_speed: f32) -> Self {
1360 self.restore_speed = restore_speed;
1361 self
1362 }
1363}
1364
1365impl RustConstructorResource for MessageBox {
1366 fn name(&self) -> &str {
1367 &self.name
1368 }
1369
1370 fn expose_type(&self) -> &str {
1371 &self.discern_type
1372 }
1373
1374 fn as_any(&self) -> &dyn Any {
1375 self
1376 }
1377
1378 fn as_any_mut(&mut self) -> &mut dyn Any {
1379 self
1380 }
1381}
1382
1383#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1385pub enum RustConstructorError {
1386 ImageGetFailed { image_path: String },
1388 ImageNotFound { image_name: String },
1390 ImageTextureNotFound { image_texture_name: String },
1392 TextNotFound { text_name: String },
1394 VariableNotFound { variable_name: String },
1396 VariableNotInt { variable_name: String },
1398 VariableNotUInt { variable_name: String },
1400 VariableNotFloat { variable_name: String },
1402 VariableNotVec { variable_name: String },
1404 VariableNotBool { variable_name: String },
1406 VariableNotString { variable_name: String },
1408 SplitTimeNotFound { split_time_name: String },
1410 SwitchAppearanceMismatch { switch_name: String, differ: u32 },
1412 SwitchNotFound { switch_name: String },
1414 MessageBoxAlreadyExists { message_box_name: String },
1416 FontGetFailed { font_path: String },
1418 FontNotFound { font_name: String },
1420 RectNotFound { rect_name: String },
1422 ResourceNotFound {
1424 resource_name: String,
1425 resource_type: String,
1426 },
1427 PageNotFound { page_name: String },
1429}
1430
1431impl std::fmt::Display for RustConstructorError {
1432 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1433 Debug::fmt(self, f)
1434 }
1435}
1436
1437impl Error for RustConstructorError {}
1438
1439impl Default for RustConstructorError {
1440 fn default() -> Self {
1441 RustConstructorError::ImageGetFailed {
1442 image_path: "".to_string(),
1443 }
1444 }
1445}
1446
1447#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1449pub enum HorizontalAlign {
1450 #[default]
1452 Left,
1453 Center,
1455 Right,
1457}
1458
1459#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1461pub enum VerticalAlign {
1462 #[default]
1464 Top,
1465 Center,
1467 Bottom,
1469}
1470
1471#[derive(Debug, Default)]
1473pub struct App {
1474 pub config: Config,
1476 pub app_text: AppText,
1478 pub rust_constructor_resource: Vec<Box<dyn RustConstructorResource>>,
1480 pub render_resource_list: Vec<RenderResource>,
1482 pub problem_list: Vec<Problem>,
1484 pub tick_interval: f32,
1486 pub current_page: String,
1488 pub timer: Timer,
1490 pub frame_times: Vec<f32>,
1492 pub last_frame_time: Option<f64>,
1494}
1495
1496impl App {
1497 #[inline]
1498 pub fn config(mut self, config: Config) -> Self {
1499 self.config = config;
1500 self
1501 }
1502
1503 #[inline]
1504 pub fn app_text(mut self, app_text: AppText) -> Self {
1505 self.app_text = app_text;
1506 self
1507 }
1508
1509 #[inline]
1510 pub fn tick_interval(mut self, tick_interval: f32) -> Self {
1511 self.tick_interval = tick_interval;
1512 self
1513 }
1514
1515 #[inline]
1516 pub fn current_page(mut self, current_page: &str) -> Self {
1517 self.current_page = current_page.to_string();
1518 self
1519 }
1520
1521 pub fn page_handler(&mut self, ctx: &egui::Context) {
1523 self.update_frame_stats(ctx);
1525 self.render_resource_list = Vec::new();
1527 self.update_timer();
1529 if let Ok(pd) = self.get_resource::<PageData>(&self.current_page.clone(), "PageData")
1530 && pd.forced_update
1531 {
1532 ctx.request_repaint();
1534 };
1535 }
1536
1537 pub fn add_page(&mut self, mut page_data: PageData) {
1539 page_data.change_page_updated = false;
1540 page_data.enter_page_updated = false;
1541 self.rust_constructor_resource.push(Box::new(page_data));
1542 }
1543
1544 pub fn switch_page(&mut self, page: &str) -> Result<(), RustConstructorError> {
1546 if self.check_resource_exists(page, "PageData") {
1547 self.current_page = page.to_string();
1548 let pd = self.get_resource_mut::<PageData>(page, "PageData").unwrap();
1549 pd.enter_page_updated = false;
1550 self.timer.start_time = self.timer.total_time;
1551 self.update_timer();
1552 Ok(())
1553 } else {
1554 self.problem_report_custom(
1555 RustConstructorError::PageNotFound {
1556 page_name: page.to_string(),
1557 },
1558 SeverityLevel::SevereWarning,
1559 self.problem_list.clone(),
1560 );
1561 Err(RustConstructorError::PageNotFound {
1562 page_name: page.to_string(),
1563 })
1564 }
1565 }
1566
1567 pub fn replace_resource_custom<T>(
1569 &self,
1570 name: &str,
1571 discern_type: &str,
1572 resource: T,
1573 mut target: Vec<Box<dyn RustConstructorResource>>,
1574 ) -> Result<(), RustConstructorError>
1575 where
1576 T: RustConstructorResource + 'static,
1577 {
1578 if let Some(index) = self
1579 .rust_constructor_resource
1580 .iter()
1581 .position(|x| x.name() == name && x.expose_type() == discern_type)
1582 {
1583 target[index] = Box::new(resource);
1584 Ok(())
1585 } else {
1586 self.problem_report_custom(
1587 RustConstructorError::ResourceNotFound {
1588 resource_name: name.to_string(),
1589 resource_type: discern_type.to_string(),
1590 },
1591 SeverityLevel::SevereWarning,
1592 self.problem_list.clone(),
1593 );
1594 Err(RustConstructorError::ResourceNotFound {
1595 resource_name: name.to_string(),
1596 resource_type: discern_type.to_string(),
1597 })
1598 }
1599 }
1600
1601 pub fn replace_resource<T>(
1603 &mut self,
1604 name: &str,
1605 discern_type: &str,
1606 resource: T,
1607 ) -> Result<(), RustConstructorError>
1608 where
1609 T: RustConstructorResource + 'static,
1610 {
1611 if let Some(index) = self
1612 .rust_constructor_resource
1613 .iter()
1614 .position(|x| x.name() == name && x.expose_type() == discern_type)
1615 {
1616 self.rust_constructor_resource[index] = Box::new(resource);
1617 Ok(())
1618 } else {
1619 self.problem_report_custom(
1620 RustConstructorError::ResourceNotFound {
1621 resource_name: name.to_string(),
1622 resource_type: discern_type.to_string(),
1623 },
1624 SeverityLevel::SevereWarning,
1625 self.problem_list.clone(),
1626 );
1627 Err(RustConstructorError::ResourceNotFound {
1628 resource_name: name.to_string(),
1629 resource_type: discern_type.to_string(),
1630 })
1631 }
1632 }
1633
1634 pub fn get_resource<T>(
1636 &self,
1637 name: &str,
1638 discern_type: &str,
1639 ) -> Result<&T, RustConstructorError>
1640 where
1641 T: RustConstructorResource + 'static,
1642 {
1643 if self.check_resource_exists(name, discern_type) {
1644 Ok(self
1645 .rust_constructor_resource
1646 .iter()
1647 .find(|resource| resource.name() == name && resource.expose_type() == discern_type)
1648 .and_then(|resource| resource.as_any().downcast_ref::<T>())
1649 .unwrap())
1650 } else {
1651 self.problem_report_custom(
1652 RustConstructorError::ResourceNotFound {
1653 resource_name: name.to_string(),
1654 resource_type: discern_type.to_string(),
1655 },
1656 SeverityLevel::SevereWarning,
1657 self.problem_list.clone(),
1658 );
1659 Err(RustConstructorError::ResourceNotFound {
1660 resource_name: name.to_string(),
1661 resource_type: discern_type.to_string(),
1662 })
1663 }
1664 }
1665
1666 pub fn get_resource_mut<T>(
1668 &mut self,
1669 name: &str,
1670 discern_type: &str,
1671 ) -> Result<&mut T, RustConstructorError>
1672 where
1673 T: RustConstructorResource + 'static,
1674 {
1675 if self.check_resource_exists(name, discern_type) {
1676 Ok(self
1677 .rust_constructor_resource
1678 .iter_mut()
1679 .find(|resource| resource.name() == name && resource.expose_type() == discern_type)
1680 .and_then(|resource| resource.as_any_mut().downcast_mut::<T>())
1681 .unwrap())
1682 } else {
1683 self.problem_report_custom(
1684 RustConstructorError::ResourceNotFound {
1685 resource_name: name.to_string(),
1686 resource_type: discern_type.to_string(),
1687 },
1688 SeverityLevel::SevereWarning,
1689 self.problem_list.clone(),
1690 );
1691 Err(RustConstructorError::ResourceNotFound {
1692 resource_name: name.to_string(),
1693 resource_type: discern_type.to_string(),
1694 })
1695 }
1696 }
1697
1698 pub fn check_resource_exists(&self, name: &str, discern_type: &str) -> bool {
1700 self.rust_constructor_resource
1701 .iter()
1702 .any(|x| x.name() == name && x.expose_type() == discern_type)
1703 }
1704
1705 pub fn add_fonts(&mut self, mut font: Font) {
1707 let mut fonts = FontDefinitions::default();
1708 if let Ok(font_read_data) = std::fs::read(font.path.clone()) {
1709 let font_data: Arc<Vec<u8>> = Arc::new(font_read_data);
1710 fonts.font_data.insert(
1711 font.name.to_owned(),
1712 Arc::new(FontData::from_owned(
1713 Arc::try_unwrap(font_data).ok().unwrap(),
1714 )),
1715 );
1716
1717 fonts
1719 .families
1720 .entry(egui::FontFamily::Proportional)
1721 .or_default()
1722 .insert(0, font.name.to_owned());
1723
1724 fonts
1725 .families
1726 .entry(egui::FontFamily::Monospace)
1727 .or_default()
1728 .insert(0, font.name.to_owned());
1729
1730 font.font_definitions = fonts;
1731 self.rust_constructor_resource.push(Box::new(font));
1732 } else {
1733 self.problem_report_custom(
1734 RustConstructorError::FontGetFailed {
1735 font_path: font.path.to_string(),
1736 },
1737 SeverityLevel::SevereWarning,
1738 self.problem_list.clone(),
1739 );
1740 };
1741 }
1742
1743 pub fn font(&mut self, name: &str) -> Result<FontDefinitions, RustConstructorError> {
1745 if let Ok(f) = self.get_resource::<Font>(name, "Font") {
1746 return Ok(f.font_definitions.clone());
1747 }
1748 self.problem_report_custom(
1749 RustConstructorError::FontNotFound {
1750 font_name: name.to_string(),
1751 },
1752 SeverityLevel::SevereWarning,
1753 self.problem_list.clone(),
1754 );
1755 Err(RustConstructorError::FontNotFound {
1756 font_name: name.to_string(),
1757 })
1758 }
1759
1760 pub fn register_all_fonts(&mut self, ctx: &egui::Context) {
1762 let mut font_definitions = egui::FontDefinitions::default();
1763 let mut font_resources = Vec::new();
1764 for i in 0..self.rust_constructor_resource.len() {
1765 if let Some(f) = self.rust_constructor_resource[i]
1766 .as_any()
1767 .downcast_ref::<Font>()
1768 {
1769 font_resources.push(f.clone());
1770 };
1771 }
1772 for i in &font_resources {
1773 let font_name = i.name.clone();
1774 if let Ok(font_def) = self.font(&font_name) {
1776 if let Some(font_data) = font_def.font_data.get(&font_name) {
1778 font_definitions
1779 .font_data
1780 .insert(font_name.clone(), Arc::clone(font_data));
1781 font_definitions
1782 .families
1783 .entry(egui::FontFamily::Name(font_name.clone().into()))
1784 .or_default()
1785 .push(font_name.clone());
1786 };
1787
1788 font_definitions
1790 .families
1791 .entry(egui::FontFamily::Proportional)
1792 .or_default()
1793 .insert(0, font_name.to_owned());
1794
1795 font_definitions
1796 .families
1797 .entry(egui::FontFamily::Monospace)
1798 .or_default()
1799 .insert(0, font_name.to_owned());
1800 };
1801 }
1802 ctx.set_fonts(font_definitions);
1803 }
1804
1805 pub fn problem_report(
1807 &mut self,
1808 problem_type: RustConstructorError,
1809 severity_level: SeverityLevel,
1810 ) {
1811 let (problem, annotation) = match problem_type.clone() {
1812 RustConstructorError::FontGetFailed { font_path } => (
1813 format!("Font get failed: {}", font_path,),
1814 "Please check if the font file exists and the path is correct.",
1815 ),
1816 RustConstructorError::FontNotFound { font_name } => (
1817 format!("Font not found: {}", font_name,),
1818 "Please check whether the font has been added.",
1819 ),
1820 RustConstructorError::ImageGetFailed { image_path } => (
1821 format!("Image get failed: {}", image_path,),
1822 "Please check whether the image path is correct and whether the image has been added.",
1823 ),
1824 RustConstructorError::ImageNotFound { image_name } => (
1825 format!("Image not found: {}", image_name,),
1826 "Please check whether the image has been added.",
1827 ),
1828 RustConstructorError::ImageTextureNotFound { image_texture_name } => (
1829 format!("Image texture not found: {}", image_texture_name,),
1830 "Please check whether the image texture has been added.",
1831 ),
1832 RustConstructorError::TextNotFound { text_name } => (
1833 format!("Text not found: {}", text_name,),
1834 "Please check whether the text has been added.",
1835 ),
1836 RustConstructorError::MessageBoxAlreadyExists { message_box_name } => (
1837 format!("Message box already exists: {}", message_box_name,),
1838 "Please check whether the code for generating the message box has been accidentally called multiple times.",
1839 ),
1840 RustConstructorError::SplitTimeNotFound { split_time_name } => (
1841 format!("Split time not found: {}", split_time_name,),
1842 "Please check whether the split time has been added.",
1843 ),
1844 RustConstructorError::SwitchAppearanceMismatch {
1845 switch_name,
1846 differ,
1847 } => (
1848 format!(
1849 "Switch appearance list's number of items is large / small {} more: {}",
1850 differ, switch_name
1851 ),
1852 "Please check whether the number of appearance list items matches the number of enabled animations.",
1853 ),
1854 RustConstructorError::SwitchNotFound { switch_name } => (
1855 format!("Switch not found: {}", switch_name,),
1856 "Please check whether the switch has been added.",
1857 ),
1858 RustConstructorError::PageNotFound { page_name } => (
1859 format!("Page not found: {}", page_name,),
1860 "Please check whether the page has been added.",
1861 ),
1862 RustConstructorError::VariableNotFound { variable_name } => (
1863 format!("Variable not found: {}", variable_name,),
1864 "Please check whether the variable has been added.",
1865 ),
1866 RustConstructorError::VariableNotBool { variable_name } => (
1867 format!("Variable is not bool: {}", variable_name,),
1868 "Please check whether the variable names and types are correct and whether there are duplicate items.",
1869 ),
1870 RustConstructorError::VariableNotFloat { variable_name } => (
1871 format!("Variable is not f32: {}", variable_name,),
1872 "Please check whether the variable names and types are correct and whether there are duplicate items.",
1873 ),
1874 RustConstructorError::VariableNotInt { variable_name } => (
1875 format!("Variable is not int: {}", variable_name,),
1876 "Please check whether the variable names and types are correct and whether there are duplicate items.",
1877 ),
1878 RustConstructorError::VariableNotString { variable_name } => (
1879 format!("Variable is not string: {}", variable_name,),
1880 "Please check whether the variable names and types are correct and whether there are duplicate items.",
1881 ),
1882 RustConstructorError::VariableNotUInt { variable_name } => (
1883 format!("Variable is not uint: {}", variable_name,),
1884 "Please check whether the variable names and types are correct and whether there are duplicate items.",
1885 ),
1886 RustConstructorError::VariableNotVec { variable_name } => (
1887 format!("Variable is not vec: {}", variable_name,),
1888 "Please check whether the variable names and types are correct and whether there are duplicate items.",
1889 ),
1890 RustConstructorError::RectNotFound { rect_name } => (
1891 format!("Rect not found: {}", rect_name,),
1892 "Please check whether the rect has been added.",
1893 ),
1894 RustConstructorError::ResourceNotFound {
1895 resource_name,
1896 resource_type,
1897 } => (
1898 format!(
1899 "Resource not found: {}(\"{}\")",
1900 resource_type, resource_name,
1901 ),
1902 "Please check whether the resource has been added.",
1903 ),
1904 };
1905 if self.config.strict_mode {
1907 panic!("{}", problem);
1908 } else {
1909 let sound = self.config.problem_report_sound.clone();
1910 std::thread::spawn(move || {
1911 play_wav(&sound).unwrap_or(0_f64);
1912 });
1913 self.problem_list.push(Problem {
1914 severity_level,
1915 problem,
1916 annotation: annotation.to_string(),
1917 report_state: ReportState {
1918 current_page: self.current_page.clone(),
1919 current_total_runtime: self.timer.total_time,
1920 current_page_runtime: self.timer.now_time,
1921 },
1922 problem_type: problem_type.clone(),
1923 });
1924 };
1925 }
1926
1927 pub fn problem_report_custom(
1929 &self,
1930 problem_type: RustConstructorError,
1931 severity_level: SeverityLevel,
1932 mut problem_storage: Vec<Problem>,
1933 ) {
1934 let (problem, annotation) = match problem_type.clone() {
1935 RustConstructorError::FontGetFailed { font_path } => (
1936 format!("Font get failed: {}", font_path,),
1937 "Please check if the font file exists and the path is correct.",
1938 ),
1939 RustConstructorError::FontNotFound { font_name } => (
1940 format!("Font not found: {}", font_name,),
1941 "Please check whether the font has been added.",
1942 ),
1943 RustConstructorError::ImageGetFailed { image_path } => (
1944 format!("Image get failed: {}", image_path,),
1945 "Please check whether the image path is correct and whether the image has been added.",
1946 ),
1947 RustConstructorError::ImageNotFound { image_name } => (
1948 format!("Image not found: {}", image_name,),
1949 "Please check whether the image has been added.",
1950 ),
1951 RustConstructorError::ImageTextureNotFound { image_texture_name } => (
1952 format!("Image texture not found: {}", image_texture_name,),
1953 "Please check whether the image texture has been added.",
1954 ),
1955 RustConstructorError::TextNotFound { text_name } => (
1956 format!("Text not found: {}", text_name,),
1957 "Please check whether the text has been added.",
1958 ),
1959 RustConstructorError::MessageBoxAlreadyExists { message_box_name } => (
1960 format!("Message box already exists: {}", message_box_name,),
1961 "Please check whether the code for generating the message box has been accidentally called multiple times.",
1962 ),
1963 RustConstructorError::SplitTimeNotFound { split_time_name } => (
1964 format!("Split time not found: {}", split_time_name,),
1965 "Please check whether the split time has been added.",
1966 ),
1967 RustConstructorError::SwitchAppearanceMismatch {
1968 switch_name,
1969 differ,
1970 } => (
1971 format!(
1972 "Switch appearance list's number of items is large / small {} more: {}",
1973 differ, switch_name
1974 ),
1975 "Please check whether the number of appearance list items matches the number of enabled animations.",
1976 ),
1977 RustConstructorError::SwitchNotFound { switch_name } => (
1978 format!("Switch not found: {}", switch_name,),
1979 "Please check whether the switch has been added.",
1980 ),
1981 RustConstructorError::PageNotFound { page_name } => (
1982 format!("Page not found: {}", page_name,),
1983 "Please check whether the page has been added.",
1984 ),
1985 RustConstructorError::VariableNotFound { variable_name } => (
1986 format!("Variable not found: {}", variable_name,),
1987 "Please check whether the variable has been added.",
1988 ),
1989 RustConstructorError::VariableNotBool { variable_name } => (
1990 format!("Variable is not bool: {}", variable_name,),
1991 "Please check whether the variable names and types are correct and whether there are duplicate items.",
1992 ),
1993 RustConstructorError::VariableNotFloat { variable_name } => (
1994 format!("Variable is not f32: {}", variable_name,),
1995 "Please check whether the variable names and types are correct and whether there are duplicate items.",
1996 ),
1997 RustConstructorError::VariableNotInt { variable_name } => (
1998 format!("Variable is not int: {}", variable_name,),
1999 "Please check whether the variable names and types are correct and whether there are duplicate items.",
2000 ),
2001 RustConstructorError::VariableNotString { variable_name } => (
2002 format!("Variable is not string: {}", variable_name,),
2003 "Please check whether the variable names and types are correct and whether there are duplicate items.",
2004 ),
2005 RustConstructorError::VariableNotUInt { variable_name } => (
2006 format!("Variable is not uint: {}", variable_name,),
2007 "Please check whether the variable names and types are correct and whether there are duplicate items.",
2008 ),
2009 RustConstructorError::VariableNotVec { variable_name } => (
2010 format!("Variable is not vec: {}", variable_name,),
2011 "Please check whether the variable names and types are correct and whether there are duplicate items.",
2012 ),
2013 RustConstructorError::RectNotFound { rect_name } => (
2014 format!("Rect not found: {}", rect_name,),
2015 "Please check whether the rect has been added.",
2016 ),
2017 RustConstructorError::ResourceNotFound {
2018 resource_name,
2019 resource_type,
2020 } => (
2021 format!(
2022 "Resource not found: {}(\"{}\")",
2023 resource_type, resource_name,
2024 ),
2025 "Please check whether the resource has been added.",
2026 ),
2027 };
2028 if self.config.strict_mode {
2030 panic!("{}", problem);
2031 } else {
2032 let sound = self.config.problem_report_sound.clone();
2033 std::thread::spawn(move || {
2034 play_wav(&sound).unwrap_or(0_f64);
2035 });
2036 problem_storage.push(Problem {
2037 severity_level,
2038 problem,
2039 annotation: annotation.to_string(),
2040 report_state: ReportState {
2041 current_page: self.current_page.clone(),
2042 current_total_runtime: self.timer.total_time,
2043 current_page_runtime: self.timer.now_time,
2044 },
2045 problem_type: problem_type.clone(),
2046 });
2047 };
2048 }
2049
2050 pub fn check_updated(&mut self, name: &str) -> Result<bool, RustConstructorError> {
2052 if self.check_resource_exists(name, "PageData") {
2053 let pd = self
2054 .get_resource::<PageData>(name, "PageData")
2055 .unwrap()
2056 .clone();
2057 if !pd.change_page_updated {
2058 self.new_page_update(name).unwrap();
2059 };
2060 Ok(pd.change_page_updated)
2061 } else {
2062 self.problem_report_custom(
2063 RustConstructorError::PageNotFound {
2064 page_name: name.to_string(),
2065 },
2066 SeverityLevel::SevereWarning,
2067 self.problem_list.clone(),
2068 );
2069 Err(RustConstructorError::PageNotFound {
2070 page_name: name.to_string(),
2071 })
2072 }
2073 }
2074
2075 pub fn check_enter_updated(&mut self, name: &str) -> Result<bool, RustConstructorError> {
2077 if self.check_resource_exists(name, "PageData") {
2078 let pd = self.get_resource_mut::<PageData>(name, "PageData").unwrap();
2079 let return_value = pd.enter_page_updated;
2080 pd.enter_page_updated = true;
2081 Ok(return_value)
2082 } else {
2083 self.problem_report_custom(
2084 RustConstructorError::PageNotFound {
2085 page_name: name.to_string(),
2086 },
2087 SeverityLevel::SevereWarning,
2088 self.problem_list.clone(),
2089 );
2090 Err(RustConstructorError::PageNotFound {
2091 page_name: name.to_string(),
2092 })
2093 }
2094 }
2095
2096 pub fn new_page_update(&mut self, name: &str) -> Result<(), RustConstructorError> {
2098 if self.check_resource_exists(name, "PageData") {
2099 self.timer.start_time = self.timer.total_time;
2100 self.update_timer();
2101 let pd = self.get_resource_mut::<PageData>(name, "PageData").unwrap();
2102 pd.change_page_updated = true;
2103 Ok(())
2104 } else {
2105 self.problem_report_custom(
2106 RustConstructorError::PageNotFound {
2107 page_name: name.to_string(),
2108 },
2109 SeverityLevel::SevereWarning,
2110 self.problem_list.clone(),
2111 );
2112 Err(RustConstructorError::PageNotFound {
2113 page_name: name.to_string(),
2114 })
2115 }
2116 }
2117
2118 pub fn update_frame_stats(&mut self, ctx: &egui::Context) {
2120 let current_time = ctx.input(|i| i.time);
2121 if let Some(last) = self.last_frame_time {
2122 let delta = (current_time - last) as f32;
2123 self.frame_times.push(delta);
2124 const MAX_SAMPLES: usize = 120;
2125 if self.frame_times.len() > MAX_SAMPLES {
2126 let remove_count = self.frame_times.len() - MAX_SAMPLES;
2127 self.frame_times.drain(0..remove_count);
2128 }
2129 }
2130 self.last_frame_time = Some(current_time);
2131 }
2132
2133 pub fn current_fps(&self) -> f32 {
2135 if self.frame_times.is_empty() {
2136 0.0
2137 } else {
2138 1.0 / (self.frame_times.iter().sum::<f32>() / self.frame_times.len() as f32)
2139 }
2140 }
2141
2142 pub fn add_split_time(&mut self, mut split_time: SplitTime) {
2144 split_time.time = [self.timer.now_time, self.timer.total_time];
2145 self.rust_constructor_resource.push(Box::new(split_time));
2146 }
2147
2148 pub fn reset_split_time(&mut self, name: &str) -> Result<(), RustConstructorError> {
2150 if self.check_resource_exists(name, "SplitTime") {
2151 let new_time = [self.timer.now_time, self.timer.total_time];
2152 let st = self
2153 .get_resource_mut::<SplitTime>(name, "SplitTime")
2154 .unwrap();
2155 st.time = new_time;
2156 Ok(())
2157 } else {
2158 self.problem_report_custom(
2159 RustConstructorError::SplitTimeNotFound {
2160 split_time_name: name.to_string(),
2161 },
2162 SeverityLevel::SevereWarning,
2163 self.problem_list.clone(),
2164 );
2165 Err(RustConstructorError::SplitTimeNotFound {
2166 split_time_name: name.to_string(),
2167 })
2168 }
2169 }
2170
2171 pub fn split_time(&self, name: &str) -> Result<[f32; 2], RustConstructorError> {
2173 if self.check_resource_exists(name, "SplitTime") {
2174 let st = self.get_resource::<SplitTime>(name, "SplitTime").unwrap();
2175 Ok(st.time)
2176 } else {
2177 self.problem_report_custom(
2178 RustConstructorError::SplitTimeNotFound {
2179 split_time_name: name.to_string(),
2180 },
2181 SeverityLevel::SevereWarning,
2182 self.problem_list.clone(),
2183 );
2184 Err(RustConstructorError::SplitTimeNotFound {
2185 split_time_name: name.to_string(),
2186 })
2187 }
2188 }
2189
2190 pub fn update_timer(&mut self) {
2192 let elapsed = self.timer.timer.elapsed();
2193 let seconds = elapsed.as_secs();
2194 let milliseconds = elapsed.subsec_millis();
2195 self.timer.total_time = seconds as f32 + milliseconds as f32 / 1000.0;
2196 self.timer.now_time = self.timer.total_time - self.timer.start_time
2197 }
2198
2199 pub fn add_custom_rect(&mut self, custom_rect: CustomRect) {
2201 self.rust_constructor_resource.push(Box::new(custom_rect));
2202 }
2203
2204 pub fn custom_rect(
2206 &mut self,
2207 name: &str,
2208 ui: &mut Ui,
2209 ctx: &egui::Context,
2210 ) -> Result<(), RustConstructorError> {
2211 if self.check_resource_exists(name, "CustomRect") {
2212 let render_resource_list = &mut self.render_resource_list.clone();
2213 let cr = self
2214 .get_resource_mut::<CustomRect>(name, "CustomRect")
2215 .unwrap();
2216 cr.reg_render_resource(render_resource_list);
2217 cr.position[0] = match cr.x_grid[1] {
2218 0 => cr.origin_position[0],
2219 _ => {
2220 (ctx.available_rect().width() as f64 / cr.x_grid[1] as f64
2221 * cr.x_grid[0] as f64) as f32
2222 + cr.origin_position[0]
2223 }
2224 };
2225 cr.position[1] = match cr.y_grid[1] {
2226 0 => cr.origin_position[1],
2227 _ => {
2228 (ctx.available_rect().height() as f64 / cr.y_grid[1] as f64
2229 * cr.y_grid[0] as f64) as f32
2230 + cr.origin_position[1]
2231 }
2232 };
2233 let pos_x = match cr.center_display.0 {
2234 HorizontalAlign::Left => cr.position[0],
2235 HorizontalAlign::Center => cr.position[0] - cr.size[0] / 2.0,
2236 HorizontalAlign::Right => cr.position[0] - cr.size[0],
2237 };
2238 let pos_y = match cr.center_display.1 {
2239 VerticalAlign::Top => cr.position[1],
2240 VerticalAlign::Center => cr.position[1] - cr.size[1] / 2.0,
2241 VerticalAlign::Bottom => cr.position[1] - cr.size[1],
2242 };
2243 ui.painter().rect(
2244 Rect::from_min_max(
2245 Pos2::new(pos_x, pos_y),
2246 Pos2::new(pos_x + cr.size[0], pos_y + cr.size[1]),
2247 ),
2248 cr.rounding,
2249 Color32::from_rgba_unmultiplied(cr.color[0], cr.color[1], cr.color[2], cr.color[3]),
2250 Stroke {
2251 width: cr.border_width,
2252 color: Color32::from_rgba_unmultiplied(
2253 cr.border_color[0],
2254 cr.border_color[1],
2255 cr.border_color[2],
2256 cr.border_color[3],
2257 ),
2258 },
2259 egui::StrokeKind::Inside,
2260 );
2261 Ok(())
2262 } else {
2263 self.problem_report_custom(
2264 RustConstructorError::RectNotFound {
2265 rect_name: name.to_string(),
2266 },
2267 SeverityLevel::SevereWarning,
2268 self.problem_list.clone(),
2269 );
2270 Err(RustConstructorError::RectNotFound {
2271 rect_name: name.to_string(),
2272 })
2273 }
2274 }
2275
2276 pub fn add_text(&mut self, text: Text) {
2278 self.rust_constructor_resource.push(Box::new(text));
2279 }
2280
2281 pub fn text(
2283 &mut self,
2284 name: &str,
2285 ui: &mut Ui,
2286 ctx: &egui::Context,
2287 ) -> Result<(), RustConstructorError> {
2288 if self.check_resource_exists(name, "Text") {
2289 let mut t = self.get_resource::<Text>(name, "Text").unwrap().clone();
2290 t.reg_render_resource(&mut self.render_resource_list);
2291 let galley: Arc<Galley> = ui.fonts_mut(|f| {
2293 f.layout(
2294 t.content.to_string(),
2295 if self.check_resource_exists(&t.font.clone(), "Font") {
2296 FontId::new(t.font_size, egui::FontFamily::Name(t.font.clone().into()))
2297 } else {
2298 if !t.font.is_empty() {
2299 self.problem_report_custom(
2300 RustConstructorError::FontNotFound {
2301 font_name: t.font.clone(),
2302 },
2303 SeverityLevel::MildWarning,
2304 self.problem_list.clone(),
2305 );
2306 };
2307 FontId::proportional(t.font_size)
2308 },
2309 Color32::from_rgba_unmultiplied(t.color[0], t.color[1], t.color[2], t.color[3]),
2310 t.wrap_width,
2311 )
2312 });
2313 let text_size = galley.size();
2314 t.position[0] = match t.x_grid[1] {
2315 0 => t.origin_position[0],
2316 _ => {
2317 (ctx.available_rect().width() as f64 / t.x_grid[1] as f64 * t.x_grid[0] as f64)
2318 as f32
2319 + t.origin_position[0]
2320 }
2321 };
2322 t.position[1] = match t.y_grid[1] {
2323 0 => t.origin_position[1],
2324 _ => {
2325 (ctx.available_rect().height() as f64 / t.y_grid[1] as f64 * t.y_grid[0] as f64)
2326 as f32
2327 + t.origin_position[1]
2328 }
2329 };
2330 let pos_x = match t.center_display.0 {
2331 HorizontalAlign::Left => t.position[0],
2332 HorizontalAlign::Center => t.position[0] - text_size.x / 2.0,
2333 HorizontalAlign::Right => t.position[0] - text_size.x,
2334 };
2335 let pos_y = match t.center_display.1 {
2336 VerticalAlign::Top => t.position[1],
2337 VerticalAlign::Center => t.position[1] - text_size.y / 2.0,
2338 VerticalAlign::Bottom => t.position[1] - text_size.y,
2339 };
2340 let position = Pos2::new(pos_x, pos_y);
2342
2343 if t.write_background {
2344 let rect = Rect::from_min_size(position, text_size);
2345 ui.painter().rect_filled(
2347 rect,
2348 t.background_rounding,
2349 Color32::from_rgba_unmultiplied(
2350 t.background_color[0],
2351 t.background_color[1],
2352 t.background_color[2],
2353 t.background_color[3],
2354 ),
2355 ); };
2357 ui.painter().galley(
2359 position,
2360 galley.clone(),
2361 Color32::from_rgba_unmultiplied(
2362 t.color[0], t.color[1], t.color[2], t.color[3], ),
2364 );
2365
2366 if t.last_frame_content != t.content {
2368 t.hyperlink_index.clear();
2369 for (text, method) in &t.hyperlink_text {
2370 let matches: Vec<(usize, &str)> = t.content.match_indices(text).collect();
2371 if let HyperlinkSelectMethod::All(url) = method {
2372 for (index, _) in matches {
2373 t.hyperlink_index
2374 .push((index, index + text.len(), url.clone()));
2375 }
2376 } else if let HyperlinkSelectMethod::Segment(list) = method {
2377 for (index, url) in list {
2378 if *index >= matches.len() {
2379 continue;
2380 };
2381 t.hyperlink_index.push((
2382 matches[*index].0,
2383 matches[*index].0 + text.len(),
2384 url.clone(),
2385 ));
2386 }
2387 };
2388 }
2389 };
2390
2391 for (start, end, _) in &t.hyperlink_index {
2393 let start_cursor = galley.pos_from_cursor(CCursor::new(*start));
2395 let end_cursor = galley.pos_from_cursor(CCursor::new(*end));
2396
2397 let start_pos = start_cursor.left_top();
2398 let end_pos = end_cursor.right_top();
2399 if start_cursor.min.y == end_cursor.min.y {
2402 let underline_y = position.y
2404 + start_pos.y
2405 + galley.rows.first().map_or(14.0, |row| row.height())
2406 - 2.0;
2407
2408 let color = Color32::from_rgba_unmultiplied(
2410 t.color[0], t.color[1], t.color[2], t.color[3],
2411 );
2412
2413 ui.painter().line_segment(
2414 [
2415 Pos2::new(position.x + start_pos.x, underline_y),
2416 Pos2::new(position.x + end_pos.x, underline_y),
2417 ],
2418 Stroke::new(t.font_size / 10_f32, color),
2419 );
2420 } else {
2421 let row_height = galley.rows.first().map_or(14.0, |row| row.height()); let start_row = (start_pos.y / row_height).round() as usize;
2426 let end_row = (end_pos.y / row_height).round() as usize;
2427
2428 for row in start_row..=end_row {
2429 let row_y = position.y + row as f32 * row_height + row_height - 2.0; if let Some(current_row) = galley.rows.get(row) {
2433 let row_rect = current_row.rect();
2434
2435 let color = Color32::from_rgba_unmultiplied(
2436 t.color[0], t.color[1], t.color[2], t.color[3],
2437 );
2438
2439 if row == start_row {
2440 ui.painter().line_segment(
2442 [
2443 Pos2::new(position.x + start_pos.x, row_y),
2444 Pos2::new(position.x + row_rect.max.x, row_y),
2445 ],
2446 Stroke::new(t.font_size / 10_f32, color),
2447 );
2448 } else if row == end_row {
2449 ui.painter().line_segment(
2451 [
2452 Pos2::new(position.x + row_rect.min.x, row_y),
2453 Pos2::new(position.x + end_pos.x, row_y),
2454 ],
2455 Stroke::new(t.font_size / 10_f32, color),
2456 );
2457 } else {
2458 ui.painter().line_segment(
2460 [
2461 Pos2::new(position.x + row_rect.min.x, row_y),
2462 Pos2::new(position.x + row_rect.max.x, row_y),
2463 ],
2464 Stroke::new(t.font_size / 10_f32, color),
2465 );
2466 };
2467 };
2468 }
2469 };
2470 }
2471
2472 if t.selectable {
2473 let rect = Rect::from_min_size(
2474 [position[0] - 20_f32, position[1] - 5_f32].into(),
2475 [text_size[0] + 40_f32, text_size[1] + 10_f32].into(),
2476 );
2477
2478 let rect2 = Rect::from_min_size(
2479 [0_f32, 0_f32].into(),
2480 [ctx.available_rect().width(), ctx.available_rect().height()].into(),
2481 );
2482
2483 let response = ui.interact(
2485 rect,
2486 egui::Id::new(format!("text_{}_click_and_drag", t.name)),
2487 egui::Sense::click_and_drag(),
2488 );
2489
2490 let response2 = ui.interact(
2491 rect2,
2492 egui::Id::new(format!("text_{}_total", t.name)),
2493 egui::Sense::click(),
2494 );
2495
2496 let cursor_at_pointer = |pointer_pos: Vec2| -> usize {
2498 let relative_pos = pointer_pos - position.to_vec2();
2499 let cursor = galley.cursor_from_pos(relative_pos);
2500 cursor.index
2501 };
2502
2503 if !response.clicked() && response2.clicked() {
2504 t.selection = None;
2505 };
2506
2507 if response.clicked() || response.drag_started() {
2508 if let Some(pointer_pos) = ui.input(|i| i.pointer.interact_pos()) {
2509 let cursor = cursor_at_pointer(pointer_pos.to_vec2());
2510 t.selection = Some((cursor, cursor));
2511 };
2512 response.request_focus();
2513 };
2514
2515 if response.dragged()
2516 && t.selection.is_some()
2517 && let Some(pointer_pos) = ui.input(|i| i.pointer.interact_pos())
2518 {
2519 let cursor = cursor_at_pointer(pointer_pos.to_vec2());
2520 if let Some((start, _)) = t.selection {
2521 t.selection = Some((start, cursor));
2522 };
2523 };
2524
2525 if response.has_focus() {
2527 let copy_triggered = ui.input(|input| {
2529 let c_released = input.key_released(egui::Key::C);
2530 let cmd_pressed = input.modifiers.command || input.modifiers.mac_cmd;
2531 let ctrl_pressed = input.modifiers.ctrl;
2532 c_released && (cmd_pressed || ctrl_pressed)
2533 });
2534 if copy_triggered && let Some((start, end)) = t.selection {
2535 let (start, end) = (start.min(end), start.max(end));
2536 let chars: Vec<char> = t.content.chars().collect();
2537 if start <= chars.len() && end <= chars.len() && start < end {
2538 let selected_text: String = chars[start..end].iter().collect();
2539 ui.ctx().copy_text(selected_text);
2540 };
2541 };
2542 };
2543
2544 if let Some((start, end)) = t.selection {
2546 let (start, end) = (start.min(end), start.max(end));
2547 if start != end {
2548 let start_cursor = galley.pos_from_cursor(CCursor::new(start));
2550 let end_cursor = galley.pos_from_cursor(CCursor::new(end));
2551
2552 let start_pos = start_cursor.left_top();
2553 let end_pos = end_cursor.right_top();
2554 if start_pos.y == end_pos.y {
2556 let rows = &galley.rows;
2559 let row_height = if !rows.is_empty() {
2560 if let Some(row) = rows.first() {
2562 row.height()
2563 } else {
2564 text_size.y / t.content.lines().count() as f32
2565 }
2566 } else {
2567 text_size.y / t.content.lines().count() as f32
2568 };
2569
2570 let selection_rect = Rect::from_min_max(
2571 Pos2::new(position.x + start_pos.x, position.y + start_pos.y),
2572 Pos2::new(
2573 position.x + end_pos.x,
2574 position.y + start_pos.y + row_height,
2575 ),
2576 );
2577 ui.painter().rect_filled(
2578 selection_rect,
2579 0.0,
2580 Color32::from_rgba_unmultiplied(0, 120, 255, 100),
2581 );
2582 } else {
2583 let rows = &galley.rows;
2585 let row_height = if !rows.is_empty() {
2586 rows[0].height()
2587 } else {
2588 text_size.y / t.content.lines().count() as f32
2589 };
2590
2591 let selection_top = position.y + start_pos.y.min(end_pos.y);
2593 let selection_bottom = position.y + start_pos.y.max(end_pos.y);
2594
2595 let start_row_index = (start_pos.y / row_height).floor() as usize;
2597 let end_row_index = (end_pos.y / row_height).floor() as usize;
2598 let (first_row_index, last_row_index) =
2599 if start_row_index <= end_row_index {
2600 (start_row_index, end_row_index)
2601 } else {
2602 (end_row_index, start_row_index)
2603 };
2604
2605 for (i, row) in rows.iter().enumerate() {
2606 let row_y = position.y + row_height * i as f32;
2607 let row_bottom = row_y + row_height;
2608 if row_bottom > selection_top && row_y <= selection_bottom {
2610 let left = if i == first_row_index {
2611 position.x + start_pos.x
2613 } else {
2614 position.x + row.rect().min.x
2616 };
2617
2618 let right = if i == last_row_index {
2619 position.x + end_pos.x
2621 } else {
2622 position.x + row.rect().max.x
2624 };
2625
2626 let selection_rect = Rect::from_min_max(
2627 Pos2::new(left, row_y),
2628 Pos2::new(right, row_bottom),
2629 );
2630
2631 if selection_rect.width() > 0.0 && selection_rect.height() > 0.0
2633 {
2634 ui.painter().rect_filled(
2635 selection_rect,
2636 0.0,
2637 Color32::from_rgba_unmultiplied(0, 120, 255, 100),
2638 );
2639 };
2640 };
2641 }
2642 };
2643 };
2644 };
2645 };
2646
2647 for (start, end, url) in &t.hyperlink_index {
2649 let start_cursor = galley.pos_from_cursor(CCursor::new(*start));
2651 let end_cursor = galley.pos_from_cursor(CCursor::new(*end));
2652
2653 let start_pos = start_cursor.left_top();
2654 let end_pos = end_cursor.right_top();
2655
2656 let row_height = galley.rows.first().map_or(14.0, |row| row.height());
2657
2658 let link_responses = if start_cursor.min.y == end_cursor.min.y {
2660 let link_rect = Rect::from_min_max(
2662 Pos2::new(position.x + start_pos.x, position.y + start_pos.y),
2663 Pos2::new(
2664 position.x + end_pos.x,
2665 position.y + start_pos.y + row_height,
2666 ),
2667 );
2668 vec![ui.interact(
2669 link_rect,
2670 egui::Id::new(format!("link_{}_{}_{}", t.name, start, end)),
2671 egui::Sense::click(),
2672 )]
2673 } else {
2674 let start_row = (start_pos.y / row_height).round() as usize;
2676 let end_row = (end_pos.y / row_height).round() as usize;
2677 let mut responses = Vec::new();
2678
2679 for row in start_row..=end_row {
2680 if let Some(current_row) = galley.rows.get(row) {
2681 let row_rect = current_row.rect();
2682 let row_y = position.y + row as f32 * row_height;
2683
2684 let link_rect = if row == start_row {
2685 Rect::from_min_max(
2687 Pos2::new(position.x + start_pos.x, row_y),
2688 Pos2::new(position.x + row_rect.max.x, row_y + row_height),
2689 )
2690 } else if row == end_row {
2691 Rect::from_min_max(
2693 Pos2::new(position.x + row_rect.min.x, row_y),
2694 Pos2::new(position.x + end_pos.x, row_y + row_height),
2695 )
2696 } else {
2697 Rect::from_min_max(
2699 Pos2::new(position.x + row_rect.min.x, row_y),
2700 Pos2::new(position.x + row_rect.max.x, row_y + row_height),
2701 )
2702 };
2703
2704 responses.push(ui.interact(
2705 link_rect,
2706 egui::Id::new(format!(
2707 "link_{}_{}_{}_row_{}",
2708 t.name, start, end, row
2709 )),
2710 egui::Sense::click(),
2711 ));
2712 };
2713 }
2714 responses
2715 };
2716
2717 let mut is_pressing_link = false;
2719 for link_response in &link_responses {
2720 if link_response.is_pointer_button_down_on() && !link_response.drag_started() {
2721 t.selection = None;
2722 if let Some(pointer_pos) = ui.input(|i| i.pointer.interact_pos()) {
2723 let relative_pos = pointer_pos - position.to_vec2();
2724 let cursor = galley.cursor_from_pos(relative_pos.to_vec2());
2725 if cursor.index >= *start && cursor.index <= *end {
2726 is_pressing_link = true;
2727 break;
2728 };
2729 };
2730 };
2731 }
2732
2733 let mut clicked_on_link = false;
2735 for link_response in &link_responses {
2736 if link_response.clicked()
2737 && let Some(pointer_pos) = ui.input(|i| i.pointer.interact_pos())
2738 {
2739 let relative_pos = pointer_pos - position.to_vec2();
2740 let cursor = galley.cursor_from_pos(relative_pos.to_vec2());
2741 if cursor.index >= *start && cursor.index <= *end {
2742 clicked_on_link = true;
2743 break;
2744 };
2745 };
2746 }
2747
2748 if clicked_on_link {
2749 if !url.is_empty() {
2751 ui.ctx().open_url(egui::OpenUrl::new_tab(url));
2752 };
2753 };
2754
2755 if is_pressing_link {
2757 if start_cursor.min.y == end_cursor.min.y {
2758 let selection_rect = Rect::from_min_max(
2760 Pos2::new(position.x + start_pos.x, position.y + start_pos.y),
2761 Pos2::new(
2762 position.x + end_pos.x,
2763 position.y
2764 + start_pos.y
2765 + galley.rows.first().map_or(14.0, |row| row.height()),
2766 ),
2767 );
2768 ui.painter().rect_filled(
2769 selection_rect,
2770 0.0,
2771 Color32::from_rgba_unmultiplied(0, 120, 255, 100),
2772 );
2773 } else {
2774 let row_height = galley.rows.first().map_or(14.0, |row| row.height());
2776 let start_row = (start_pos.y / row_height).round() as usize;
2777 let end_row = (end_pos.y / row_height).round() as usize;
2778
2779 for row in start_row..=end_row {
2780 if let Some(current_row) = galley.rows.get(row) {
2781 let row_rect = current_row.rect();
2782
2783 if row == start_row {
2784 let selection_rect = Rect::from_min_max(
2786 Pos2::new(
2787 position.x + start_pos.x,
2788 position.y + row as f32 * row_height,
2789 ),
2790 Pos2::new(
2791 position.x + row_rect.max.x,
2792 position.y + row as f32 * row_height + row_height,
2793 ),
2794 );
2795 ui.painter().rect_filled(
2796 selection_rect,
2797 0.0,
2798 Color32::from_rgba_unmultiplied(0, 120, 255, 100),
2799 );
2800 } else if row == end_row {
2801 let selection_rect = Rect::from_min_max(
2803 Pos2::new(
2804 position.x + row_rect.min.x,
2805 position.y + row as f32 * row_height,
2806 ),
2807 Pos2::new(
2808 position.x + end_pos.x,
2809 position.y + row as f32 * row_height + row_height,
2810 ),
2811 );
2812 ui.painter().rect_filled(
2813 selection_rect,
2814 0.0,
2815 Color32::from_rgba_unmultiplied(0, 120, 255, 100),
2816 );
2817 } else {
2818 let selection_rect = Rect::from_min_max(
2820 Pos2::new(
2821 position.x + row_rect.min.x,
2822 position.y + row as f32 * row_height,
2823 ),
2824 Pos2::new(
2825 position.x + row_rect.max.x,
2826 position.y + row as f32 * row_height + row_height,
2827 ),
2828 );
2829 ui.painter().rect_filled(
2830 selection_rect,
2831 0.0,
2832 Color32::from_rgba_unmultiplied(0, 120, 255, 100),
2833 );
2834 };
2835 };
2836 }
2837 };
2838 };
2839 }
2840 t.last_frame_content = t.content.clone();
2841 self.replace_resource(name, "Text", t).unwrap();
2842 Ok(())
2843 } else {
2844 self.problem_report_custom(
2845 RustConstructorError::TextNotFound {
2846 text_name: name.to_string(),
2847 },
2848 SeverityLevel::SevereWarning,
2849 self.problem_list.clone(),
2850 );
2851 Err(RustConstructorError::TextNotFound {
2852 text_name: name.to_string(),
2853 })
2854 }
2855 }
2856
2857 pub fn get_text_size(&self, name: &str, ui: &mut Ui) -> Result<[f32; 2], RustConstructorError> {
2859 if self.check_resource_exists(name, "Text") {
2860 let t = self.get_resource::<Text>(name, "Text").unwrap();
2861 let galley = ui.fonts_mut(|f| {
2862 f.layout(
2863 t.content.to_string(),
2864 FontId::proportional(t.font_size),
2865 Color32::from_rgba_unmultiplied(t.color[0], t.color[1], t.color[2], t.color[3]),
2866 t.wrap_width,
2867 )
2868 });
2869 Ok([galley.size().x, galley.size().y])
2870 } else {
2871 self.problem_report_custom(
2872 RustConstructorError::TextNotFound {
2873 text_name: name.to_string(),
2874 },
2875 SeverityLevel::SevereWarning,
2876 self.problem_list.clone(),
2877 );
2878 Err(RustConstructorError::TextNotFound {
2879 text_name: name.to_string(),
2880 })
2881 }
2882 }
2883
2884 pub fn add_var(&mut self, variable: Variable) {
2886 self.rust_constructor_resource.push(Box::new(variable));
2887 }
2888
2889 pub fn modify_var<T: Into<Value>>(
2891 &mut self,
2892 name: &str,
2893 value: T,
2894 ) -> Result<(), RustConstructorError> {
2895 if self.check_resource_exists(name, "Variable") {
2896 let v = self.get_resource_mut::<Variable>(name, "Variable").unwrap();
2897 v.value = value.into();
2898 Ok(())
2899 } else {
2900 self.problem_report_custom(
2901 RustConstructorError::VariableNotFound {
2902 variable_name: name.to_string(),
2903 },
2904 SeverityLevel::SevereWarning,
2905 self.problem_list.clone(),
2906 );
2907 Err(RustConstructorError::VariableNotFound {
2908 variable_name: name.to_string(),
2909 })
2910 }
2911 }
2912
2913 pub fn var(&self, name: &str) -> Result<Value, RustConstructorError> {
2915 if self.check_resource_exists(name, "Variable") {
2916 let v = self.get_resource::<Variable>(name, "Variable").unwrap();
2917 Ok(v.value.clone())
2918 } else {
2919 self.problem_report_custom(
2920 RustConstructorError::VariableNotFound {
2921 variable_name: name.to_string(),
2922 },
2923 SeverityLevel::SevereWarning,
2924 self.problem_list.clone(),
2925 );
2926 Err(RustConstructorError::VariableNotFound {
2927 variable_name: name.to_string(),
2928 })
2929 }
2930 }
2931
2932 pub fn var_i(&self, name: &str) -> Result<i32, RustConstructorError> {
2934 if self.check_resource_exists(name, "Variable") {
2935 let v = self.get_resource::<Variable>(name, "Variable").unwrap();
2936 match &v.value {
2937 Value::Int(i) => Ok(*i),
2939 _ => {
2940 self.problem_report_custom(
2941 RustConstructorError::VariableNotInt {
2942 variable_name: name.to_string(),
2943 },
2944 SeverityLevel::SevereWarning,
2945 self.problem_list.clone(),
2946 );
2947 Err(RustConstructorError::VariableNotInt {
2948 variable_name: name.to_string(),
2949 })
2950 }
2951 }
2952 } else {
2953 self.problem_report_custom(
2954 RustConstructorError::VariableNotFound {
2955 variable_name: name.to_string(),
2956 },
2957 SeverityLevel::SevereWarning,
2958 self.problem_list.clone(),
2959 );
2960 Err(RustConstructorError::VariableNotFound {
2961 variable_name: name.to_string(),
2962 })
2963 }
2964 }
2965
2966 pub fn var_u(&self, name: &str) -> Result<u32, RustConstructorError> {
2968 if self.check_resource_exists(name, "Variable") {
2969 let v = self.get_resource::<Variable>(name, "Variable").unwrap();
2970 match &v.value {
2971 Value::UInt(u) => Ok(*u),
2973 _ => {
2974 self.problem_report_custom(
2975 RustConstructorError::VariableNotUInt {
2976 variable_name: name.to_string(),
2977 },
2978 SeverityLevel::SevereWarning,
2979 self.problem_list.clone(),
2980 );
2981 Err(RustConstructorError::VariableNotUInt {
2982 variable_name: name.to_string(),
2983 })
2984 }
2985 }
2986 } else {
2987 self.problem_report_custom(
2988 RustConstructorError::VariableNotFound {
2989 variable_name: name.to_string(),
2990 },
2991 SeverityLevel::SevereWarning,
2992 self.problem_list.clone(),
2993 );
2994 Err(RustConstructorError::VariableNotFound {
2995 variable_name: name.to_string(),
2996 })
2997 }
2998 }
2999
3000 pub fn var_f(&self, name: &str) -> Result<f32, RustConstructorError> {
3002 if self.check_resource_exists(name, "Variable") {
3003 let v = self.get_resource::<Variable>(name, "Variable").unwrap();
3004 match &v.value {
3005 Value::Float(f) => Ok(*f),
3007 _ => {
3008 self.problem_report_custom(
3009 RustConstructorError::VariableNotFloat {
3010 variable_name: name.to_string(),
3011 },
3012 SeverityLevel::SevereWarning,
3013 self.problem_list.clone(),
3014 );
3015 Err(RustConstructorError::VariableNotFloat {
3016 variable_name: name.to_string(),
3017 })
3018 }
3019 }
3020 } else {
3021 self.problem_report_custom(
3022 RustConstructorError::VariableNotFound {
3023 variable_name: name.to_string(),
3024 },
3025 SeverityLevel::SevereWarning,
3026 self.problem_list.clone(),
3027 );
3028 Err(RustConstructorError::VariableNotFound {
3029 variable_name: name.to_string(),
3030 })
3031 }
3032 }
3033
3034 pub fn var_b(&self, name: &str) -> Result<bool, RustConstructorError> {
3036 if self.check_resource_exists(name, "Variable") {
3037 let v = self.get_resource::<Variable>(name, "Variable").unwrap();
3038 match &v.value {
3039 Value::Bool(b) => Ok(*b),
3041 _ => {
3042 self.problem_report_custom(
3043 RustConstructorError::VariableNotBool {
3044 variable_name: name.to_string(),
3045 },
3046 SeverityLevel::SevereWarning,
3047 self.problem_list.clone(),
3048 );
3049 Err(RustConstructorError::VariableNotBool {
3050 variable_name: name.to_string(),
3051 })
3052 }
3053 }
3054 } else {
3055 self.problem_report_custom(
3056 RustConstructorError::VariableNotFound {
3057 variable_name: name.to_string(),
3058 },
3059 SeverityLevel::SevereWarning,
3060 self.problem_list.clone(),
3061 );
3062 Err(RustConstructorError::VariableNotFound {
3063 variable_name: name.to_string(),
3064 })
3065 }
3066 }
3067
3068 pub fn var_v(&self, name: &str) -> Result<Vec<Value>, RustConstructorError> {
3070 if self.check_resource_exists(name, "Variable") {
3071 let v = self.get_resource::<Variable>(name, "Variable").unwrap();
3072 match &v.value {
3073 Value::Vec(v) => Ok(v.clone()),
3075 _ => {
3076 self.problem_report_custom(
3077 RustConstructorError::VariableNotVec {
3078 variable_name: name.to_string(),
3079 },
3080 SeverityLevel::SevereWarning,
3081 self.problem_list.clone(),
3082 );
3083 Err(RustConstructorError::VariableNotVec {
3084 variable_name: name.to_string(),
3085 })
3086 }
3087 }
3088 } else {
3089 self.problem_report_custom(
3090 RustConstructorError::VariableNotFound {
3091 variable_name: name.to_string(),
3092 },
3093 SeverityLevel::SevereWarning,
3094 self.problem_list.clone(),
3095 );
3096 Err(RustConstructorError::VariableNotFound {
3097 variable_name: name.to_string(),
3098 })
3099 }
3100 }
3101
3102 pub fn var_s(&self, name: &str) -> Result<String, RustConstructorError> {
3104 if self.check_resource_exists(name, "Variable") {
3105 let v = self.get_resource::<Variable>(name, "Variable").unwrap();
3106 match &v.value {
3107 Value::String(s) => Ok(s.clone()),
3109 _ => {
3110 self.problem_report_custom(
3111 RustConstructorError::VariableNotString {
3112 variable_name: name.to_string(),
3113 },
3114 SeverityLevel::SevereWarning,
3115 self.problem_list.clone(),
3116 );
3117 Err(RustConstructorError::VariableNotString {
3118 variable_name: name.to_string(),
3119 })
3120 }
3121 }
3122 } else {
3123 self.problem_report_custom(
3124 RustConstructorError::VariableNotFound {
3125 variable_name: name.to_string(),
3126 },
3127 SeverityLevel::SevereWarning,
3128 self.problem_list.clone(),
3129 );
3130 Err(RustConstructorError::VariableNotFound {
3131 variable_name: name.to_string(),
3132 })
3133 }
3134 }
3135
3136 pub fn var_decode_b(&self, target: Value) -> Result<bool, RustConstructorError> {
3138 match target {
3139 Value::Bool(b) => {
3140 Ok(b)
3142 }
3143 _ => {
3144 self.problem_report_custom(
3145 RustConstructorError::VariableNotBool {
3146 variable_name: format!("{:?}", target),
3147 },
3148 SeverityLevel::SevereWarning,
3149 self.problem_list.clone(),
3150 );
3151 Err(RustConstructorError::VariableNotBool {
3152 variable_name: format!("{:?}", target),
3153 })
3154 }
3155 }
3156 }
3157
3158 pub fn var_decode_i(&self, target: Value) -> Result<i32, RustConstructorError> {
3160 match target {
3161 Value::Int(i) => {
3162 Ok(i)
3164 }
3165 _ => {
3166 self.problem_report_custom(
3167 RustConstructorError::VariableNotInt {
3168 variable_name: format!("{:?}", target),
3169 },
3170 SeverityLevel::SevereWarning,
3171 self.problem_list.clone(),
3172 );
3173 Err(RustConstructorError::VariableNotInt {
3174 variable_name: format!("{:?}", target),
3175 })
3176 }
3177 }
3178 }
3179
3180 pub fn var_decode_u(&self, target: Value) -> Result<u32, RustConstructorError> {
3182 match target {
3183 Value::UInt(u) => {
3184 Ok(u)
3186 }
3187 _ => {
3188 self.problem_report_custom(
3189 RustConstructorError::VariableNotUInt {
3190 variable_name: format!("{:?}", target),
3191 },
3192 SeverityLevel::SevereWarning,
3193 self.problem_list.clone(),
3194 );
3195 Err(RustConstructorError::VariableNotUInt {
3196 variable_name: format!("{:?}", target),
3197 })
3198 }
3199 }
3200 }
3201
3202 pub fn var_decode_f(&self, target: Value) -> Result<f32, RustConstructorError> {
3204 match target {
3205 Value::Float(f) => {
3206 Ok(f)
3208 }
3209 _ => {
3210 self.problem_report_custom(
3211 RustConstructorError::VariableNotFloat {
3212 variable_name: format!("{:?}", target),
3213 },
3214 SeverityLevel::SevereWarning,
3215 self.problem_list.clone(),
3216 );
3217 Err(RustConstructorError::VariableNotFloat {
3218 variable_name: format!("{:?}", target),
3219 })
3220 }
3221 }
3222 }
3223
3224 pub fn var_decode_s(&self, target: Value) -> Result<String, RustConstructorError> {
3226 match target {
3227 Value::String(s) => {
3228 Ok(s)
3230 }
3231 _ => {
3232 self.problem_report_custom(
3233 RustConstructorError::VariableNotString {
3234 variable_name: format!("{:?}", target),
3235 },
3236 SeverityLevel::SevereWarning,
3237 self.problem_list.clone(),
3238 );
3239 Err(RustConstructorError::VariableNotString {
3240 variable_name: format!("{:?}", target),
3241 })
3242 }
3243 }
3244 }
3245
3246 pub fn var_decode_v(&self, target: Value) -> Result<Vec<Value>, RustConstructorError> {
3248 match target {
3249 Value::Vec(v) => {
3250 Ok(v)
3252 }
3253 _ => {
3254 self.problem_report_custom(
3255 RustConstructorError::VariableNotVec {
3256 variable_name: format!("{:?}", target),
3257 },
3258 SeverityLevel::SevereWarning,
3259 self.problem_list.clone(),
3260 );
3261 Err(RustConstructorError::VariableNotVec {
3262 variable_name: format!("{:?}", target),
3263 })
3264 }
3265 }
3266 }
3267
3268 pub fn add_image_texture(
3270 &mut self,
3271 mut image_texture: ImageTexture,
3272 path: &str,
3273 flip: [bool; 2],
3274 ctx: &egui::Context,
3275 ) {
3276 if let Ok(mut file) = File::open(path) {
3277 let mut buffer = Vec::new();
3278 file.read_to_end(&mut buffer).unwrap();
3279 let img_bytes = buffer;
3280 let img = image::load_from_memory(&img_bytes).unwrap();
3281 let color_data = match flip {
3282 [true, true] => img.fliph().flipv().into_rgba8(),
3283 [true, false] => img.fliph().into_rgba8(),
3284 [false, true] => img.flipv().into_rgba8(),
3285 _ => img.into_rgba8(),
3286 };
3287 let (w, h) = (color_data.width(), color_data.height());
3288 let raw_data: Vec<u8> = color_data.into_raw();
3289
3290 let color_image =
3291 egui::ColorImage::from_rgba_unmultiplied([w as usize, h as usize], &raw_data);
3292 let loaded_image_texture = ctx.load_texture(
3293 image_texture.name.clone(),
3294 color_image,
3295 TextureOptions::LINEAR,
3296 );
3297 image_texture.texture = Some(DebugTextureHandle::new(loaded_image_texture));
3298 image_texture.cite_path = path.to_string();
3299 self.rust_constructor_resource.push(Box::new(image_texture));
3300 } else {
3301 self.problem_report_custom(
3302 RustConstructorError::ImageGetFailed {
3303 image_path: path.to_string(),
3304 },
3305 SeverityLevel::SevereWarning,
3306 self.problem_list.clone(),
3307 );
3308 };
3309 }
3310
3311 pub fn image_texture(
3313 &self,
3314 name: &str,
3315 ) -> Result<Option<DebugTextureHandle>, RustConstructorError> {
3316 if self.check_resource_exists(name, "ImageTexture") {
3317 let it = self
3318 .get_resource::<ImageTexture>(name, "ImageTexture")
3319 .unwrap();
3320 Ok(it.texture.clone())
3321 } else {
3322 self.problem_report_custom(
3323 RustConstructorError::ImageNotFound {
3324 image_name: name.to_string(),
3325 },
3326 SeverityLevel::SevereWarning,
3327 self.problem_list.clone(),
3328 );
3329 Err(RustConstructorError::ImageNotFound {
3330 image_name: name.to_string(),
3331 })
3332 }
3333 }
3334
3335 pub fn reset_image_texture(
3337 &mut self,
3338 name: &str,
3339 path: &str,
3340 flip: [bool; 2],
3341 ctx: &egui::Context,
3342 ) -> Result<(), RustConstructorError> {
3343 if self.check_resource_exists(name, "ImageTexture") {
3344 let it = self
3345 .get_resource_mut::<ImageTexture>(name, "ImageTexture")
3346 .unwrap();
3347 if let Ok(mut file) = File::open(path) {
3348 let mut buffer = Vec::new();
3349 file.read_to_end(&mut buffer).unwrap();
3350 let img_bytes = buffer;
3351 let img = image::load_from_memory(&img_bytes).unwrap();
3352 let color_data = match flip {
3353 [true, true] => img.fliph().flipv().into_rgba8(),
3354 [true, false] => img.fliph().into_rgba8(),
3355 [false, true] => img.flipv().into_rgba8(),
3356 _ => img.into_rgba8(),
3357 };
3358 let (w, h) = (color_data.width(), color_data.height());
3359 let raw_data: Vec<u8> = color_data.into_raw();
3360
3361 let color_image =
3362 egui::ColorImage::from_rgba_unmultiplied([w as usize, h as usize], &raw_data);
3363 let image_texture =
3364 ctx.load_texture(it.name.clone(), color_image, TextureOptions::LINEAR);
3365 it.texture = Some(DebugTextureHandle::new(image_texture));
3366 it.cite_path = path.to_string();
3367 } else {
3368 self.problem_report_custom(
3369 RustConstructorError::ImageGetFailed {
3370 image_path: path.to_string(),
3371 },
3372 SeverityLevel::SevereWarning,
3373 self.problem_list.clone(),
3374 );
3375 };
3376 Ok(())
3377 } else {
3378 self.problem_report_custom(
3379 RustConstructorError::ImageTextureNotFound {
3380 image_texture_name: name.to_string(),
3381 },
3382 SeverityLevel::SevereWarning,
3383 self.problem_list.clone(),
3384 );
3385 Err(RustConstructorError::ImageTextureNotFound {
3386 image_texture_name: name.to_string(),
3387 })
3388 }
3389 }
3390
3391 pub fn add_image(
3393 &mut self,
3394 mut image: Image,
3395 image_texture_name: &str,
3396 ) -> Result<(), RustConstructorError> {
3397 if self.check_resource_exists(image_texture_name, "ImageTexture") {
3398 let it = self
3399 .get_resource::<ImageTexture>(image_texture_name, "ImageTexture")
3400 .unwrap();
3401 image.texture = it.texture.clone();
3402 image.cite_texture = it.name.clone();
3403 image.last_frame_cite_texture = it.name.clone();
3404 self.rust_constructor_resource.push(Box::new(image));
3405 Ok(())
3406 } else {
3407 self.problem_report_custom(
3408 RustConstructorError::ImageTextureNotFound {
3409 image_texture_name: image_texture_name.to_string(),
3410 },
3411 SeverityLevel::SevereWarning,
3412 self.problem_list.clone(),
3413 );
3414 Err(RustConstructorError::ImageTextureNotFound {
3415 image_texture_name: image_texture_name.to_string(),
3416 })
3417 }
3418 }
3419
3420 pub fn image(
3422 &mut self,
3423 name: &str,
3424 ui: &mut Ui,
3425 ctx: &egui::Context,
3426 ) -> Result<(), RustConstructorError> {
3427 if self.check_resource_exists(name, "Image") {
3428 let mut im = self
3429 .get_resource_mut::<Image>(name, "Image")
3430 .unwrap()
3431 .clone();
3432 if im.cite_texture != im.last_frame_cite_texture {
3433 if self.check_resource_exists(&im.cite_texture, "ImageTexture") {
3434 let it = self
3435 .get_resource::<ImageTexture>(&im.cite_texture, "ImageTexture")
3436 .unwrap();
3437 im.texture = it.texture.clone();
3438 } else {
3439 self.problem_report_custom(
3440 RustConstructorError::ImageTextureNotFound {
3441 image_texture_name: im.cite_texture.clone(),
3442 },
3443 SeverityLevel::MildWarning,
3444 self.problem_list.clone(),
3445 );
3446 };
3447 };
3448 im.reg_render_resource(&mut self.render_resource_list);
3449 im.position[0] = match im.x_grid[1] {
3450 0 => im.origin_position[0],
3451 _ => {
3452 (ctx.available_rect().width() as f64 / im.x_grid[1] as f64
3453 * im.x_grid[0] as f64) as f32
3454 + im.origin_position[0]
3455 }
3456 };
3457 im.position[1] = match im.y_grid[1] {
3458 0 => im.origin_position[1],
3459 _ => {
3460 (ctx.available_rect().height() as f64 / im.y_grid[1] as f64
3461 * im.y_grid[0] as f64) as f32
3462 + im.origin_position[1]
3463 }
3464 };
3465 match im.center_display.0 {
3466 HorizontalAlign::Left => {}
3467 HorizontalAlign::Center => im.position[0] -= im.size[0] / 2.0,
3468 HorizontalAlign::Right => im.position[0] -= im.size[0],
3469 };
3470 match im.center_display.1 {
3471 VerticalAlign::Top => {}
3472 VerticalAlign::Center => im.position[1] -= im.size[1] / 2.0,
3473 VerticalAlign::Bottom => im.position[1] -= im.size[1],
3474 };
3475 if let Some(texture) = &im.texture {
3476 let rect = Rect::from_min_size(
3477 Pos2::new(im.position[0], im.position[1]),
3478 Vec2::new(im.size[0], im.size[1]),
3479 );
3480 let color = if im.use_overlay_color {
3481 Color32::from_rgba_unmultiplied(
3483 im.overlay_color[0],
3484 im.overlay_color[1],
3485 im.overlay_color[2],
3486 (im.alpha as f32 * im.overlay_color[3] as f32 / 255.0) as u8,
3488 )
3489 } else {
3490 Color32::from_white_alpha(im.alpha)
3491 };
3492
3493 egui::Image::new(egui::ImageSource::Texture((&texture.0).into()))
3495 .tint(color)
3496 .paint_at(ui, rect)
3497 };
3498 im.last_frame_cite_texture = im.cite_texture.clone();
3499 self.replace_resource(name, "Image", im).unwrap();
3500 Ok(())
3501 } else {
3502 self.problem_report_custom(
3503 RustConstructorError::ImageNotFound {
3504 image_name: name.to_string(),
3505 },
3506 SeverityLevel::SevereWarning,
3507 self.problem_list.clone(),
3508 );
3509 Err(RustConstructorError::ImageNotFound {
3510 image_name: name.to_string(),
3511 })
3512 }
3513 }
3514
3515 pub fn add_message_box(
3517 &mut self,
3518 mut message_box: MessageBox,
3519 title_name: &str,
3520 content_name: &str,
3521 image_name: &str,
3522 sound_path: &str,
3523 ) -> Result<(), RustConstructorError> {
3524 if !self.check_resource_exists(&message_box.name, "MessageBox") {
3525 message_box.exist = true;
3526 message_box.memory_offset = 0_f32;
3527
3528 if self.check_resource_exists(image_name, "Image") {
3529 let im = self.get_resource_mut::<Image>(image_name, "Image").unwrap();
3530 im.size = [message_box.size[1] - 15_f32, message_box.size[1] - 15_f32];
3531 im.center_display = (HorizontalAlign::Left, VerticalAlign::Center);
3532 im.x_grid = [1, 1];
3533 im.y_grid = [0, 1];
3534 im.name = format!("MessageBox{}", im.name);
3535 message_box.image_name = im.name.to_string();
3536 } else {
3537 self.problem_report_custom(
3538 RustConstructorError::ImageNotFound {
3539 image_name: image_name.to_string(),
3540 },
3541 SeverityLevel::SevereWarning,
3542 self.problem_list.clone(),
3543 );
3544 return Err(RustConstructorError::ImageNotFound {
3545 image_name: image_name.to_string(),
3546 });
3547 }
3548
3549 if self.check_resource_exists(title_name, "Text") {
3550 let t = self.get_resource_mut::<Text>(title_name, "Text").unwrap();
3551 t.x_grid = [1, 1];
3552 t.y_grid = [0, 1];
3553 t.center_display = (HorizontalAlign::Left, VerticalAlign::Top);
3554 t.wrap_width = message_box.size[0] - message_box.size[1] + 5_f32;
3555 t.name = format!("MessageBox{}", t.name);
3556 message_box.title_name = t.name.to_string();
3557 } else {
3558 self.problem_report_custom(
3559 RustConstructorError::TextNotFound {
3560 text_name: title_name.to_string(),
3561 },
3562 SeverityLevel::SevereWarning,
3563 self.problem_list.clone(),
3564 );
3565 return Err(RustConstructorError::TextNotFound {
3566 text_name: title_name.to_string(),
3567 });
3568 }
3569
3570 if self.check_resource_exists(content_name, "Text") {
3571 let t = self.get_resource_mut::<Text>(content_name, "Text").unwrap();
3572 t.center_display = (HorizontalAlign::Left, VerticalAlign::Top);
3573 t.x_grid = [1, 1];
3574 t.y_grid = [0, 1];
3575 t.wrap_width = message_box.size[0] - message_box.size[1] + 5_f32;
3576 t.name = format!("MessageBox{}", t.name);
3577 message_box.content_name = t.name.to_string();
3578 } else {
3579 self.problem_report_custom(
3580 RustConstructorError::TextNotFound {
3581 text_name: content_name.to_string(),
3582 },
3583 SeverityLevel::SevereWarning,
3584 self.problem_list.clone(),
3585 );
3586 return Err(RustConstructorError::TextNotFound {
3587 text_name: content_name.to_string(),
3588 });
3589 }
3590
3591 self.rust_constructor_resource
3592 .push(Box::new(message_box.clone()));
3593
3594 if !message_box.keep_existing {
3595 self.add_split_time(
3596 SplitTime::default().name(&format!("MessageBox{}", message_box.name)),
3597 );
3598 };
3599
3600 self.add_split_time(
3601 SplitTime::default().name(&format!("MessageBox{}Animation", message_box.name)),
3602 );
3603
3604 self.add_custom_rect(
3605 CustomRect::default()
3606 .name(&format!("MessageBox{}", message_box.name))
3607 .origin_position(0_f32, 0_f32)
3608 .size(message_box.size[0], message_box.size[1])
3609 .rounding(20_f32)
3610 .x_grid(1, 1)
3611 .y_grid(0, 1)
3612 .center_display(HorizontalAlign::Left, VerticalAlign::Top)
3613 .color(100, 100, 100, 125)
3614 .border_width(0_f32),
3615 );
3616
3617 self.add_image(
3618 Image::default()
3619 .name(&format!("MessageBox{}Close", message_box.name))
3620 .use_overlay_color(false)
3621 .size(30_f32, 30_f32)
3622 .center_display(HorizontalAlign::Center, VerticalAlign::Center),
3623 "CloseMessageBox",
3624 )
3625 .unwrap();
3626
3627 self.add_switch(
3628 Switch::default()
3629 .name(&format!("MessageBox{}Close", message_box.name))
3630 .appearance(vec![
3631 SwitchData {
3632 texture: "CloseMessageBox".to_string(),
3633 color: [255, 255, 255, 0],
3634 text: String::new(),
3635 hint_text: String::new(),
3636 },
3637 SwitchData {
3638 texture: "CloseMessageBox".to_string(),
3639 color: [180, 180, 180, 200],
3640 text: String::new(),
3641 hint_text: String::new(),
3642 },
3643 SwitchData {
3644 texture: "CloseMessageBox".to_string(),
3645 color: [255, 255, 255, 200],
3646 text: String::new(),
3647 hint_text: String::new(),
3648 },
3649 SwitchData {
3650 texture: "CloseMessageBox".to_string(),
3651 color: [180, 180, 180, 200],
3652 text: String::new(),
3653 hint_text: String::new(),
3654 },
3655 ])
3656 .enable_hover_click_image(false, true)
3657 .click_method(vec![SwitchClickAction {
3658 click_method: PointerButton::Primary,
3659 action: true,
3660 }])
3661 .sound_path(sound_path),
3662 &format!("MessageBox{}Close", message_box.name),
3663 "",
3664 )
3665 .unwrap();
3666 Ok(())
3667 } else {
3668 self.problem_report_custom(
3669 RustConstructorError::MessageBoxAlreadyExists {
3670 message_box_name: message_box.name.to_string(),
3671 },
3672 SeverityLevel::SevereWarning,
3673 self.problem_list.clone(),
3674 );
3675 Err(RustConstructorError::MessageBoxAlreadyExists {
3676 message_box_name: message_box.name.to_string(),
3677 })
3678 }
3679 }
3680
3681 pub fn message_box_display(&mut self, ctx: &egui::Context, ui: &mut Ui) {
3683 let mut offset = 0_f32;
3684 let mut delete_count = 0;
3685 let mut index_list = Vec::new();
3686 for i in 0..self.rust_constructor_resource.len() {
3687 if self.rust_constructor_resource[i]
3688 .as_any()
3689 .downcast_ref::<MessageBox>()
3690 .is_some()
3691 {
3692 index_list.push(i);
3693 };
3694 }
3695 for u in 0..index_list.len() {
3696 let mut deleted = false;
3697 let i = u - delete_count;
3698 let mut mb = self.rust_constructor_resource[index_list[i]]
3699 .as_any()
3700 .downcast_ref::<MessageBox>()
3701 .unwrap()
3702 .clone();
3703 let mut im1 = self
3704 .get_resource::<Image>(&mb.image_name, "Image")
3705 .unwrap()
3706 .clone();
3707 let mut cr = self
3708 .get_resource::<CustomRect>(&format!("MessageBox{}", mb.name), "CustomRect")
3709 .unwrap()
3710 .clone();
3711 let mut t1 = self
3712 .get_resource::<Text>(&mb.title_name, "Text")
3713 .unwrap()
3714 .clone();
3715 let mut t2 = self
3716 .get_resource::<Text>(&mb.content_name, "Text")
3717 .unwrap()
3718 .clone();
3719 let mut s = self
3720 .get_resource::<Switch>(&format!("MessageBox{}Close", mb.name), "Switch")
3721 .unwrap()
3722 .clone();
3723 let mut im2 = self
3724 .get_resource::<Image>(&format!("MessageBox{}Close", mb.name), "Image")
3725 .unwrap()
3726 .clone();
3727 mb.reg_render_resource(&mut self.render_resource_list.clone());
3728 if mb.size[1]
3729 < self.get_text_size(&mb.title_name.clone(), ui).unwrap()[1]
3730 + self.get_text_size(&mb.content_name.clone(), ui).unwrap()[1]
3731 + 10_f32
3732 {
3733 mb.size[1] = self.get_text_size(&mb.title_name.clone(), ui).unwrap()[1]
3734 + self.get_text_size(&mb.content_name.clone(), ui).unwrap()[1]
3735 + 10_f32;
3736 cr.size[1] = mb.size[1];
3737 im1.size = [mb.size[1] - 15_f32, mb.size[1] - 15_f32];
3738 t1.wrap_width = mb.size[0] - mb.size[1] + 5_f32;
3739 t2.wrap_width = mb.size[0] - mb.size[1] + 5_f32;
3740 };
3741 if self.timer.total_time
3742 - self
3743 .split_time(&format!("MessageBox{}Animation", mb.name))
3744 .unwrap()[1]
3745 >= self.tick_interval
3746 {
3747 self.reset_split_time(&format!("MessageBox{}Animation", mb.name))
3748 .unwrap();
3749 if offset != mb.memory_offset {
3750 if mb.memory_offset < offset {
3751 if mb.memory_offset + mb.restore_speed >= offset {
3752 mb.memory_offset = offset;
3753 } else {
3754 mb.memory_offset += mb.restore_speed;
3755 };
3756 } else if mb.memory_offset - mb.restore_speed <= offset {
3757 mb.memory_offset = offset;
3758 } else {
3759 mb.memory_offset -= mb.restore_speed;
3760 };
3761 };
3762 if cr.origin_position[0] != -mb.size[0] - 5_f32 {
3763 if mb.exist {
3764 if cr.origin_position[0] - mb.speed <= -mb.size[0] - 5_f32 {
3765 cr.origin_position[0] = -mb.size[0] - 5_f32;
3766 if self.check_resource_exists(
3767 &format!("MessageBox{}", mb.name),
3768 "SplitTime",
3769 ) {
3770 self.reset_split_time(&format!("MessageBox{}", mb.name))
3771 .unwrap();
3772 };
3773 } else {
3774 cr.origin_position[0] -= mb.speed;
3775 };
3776 } else if cr.origin_position[0] + mb.speed >= 15_f32 {
3777 cr.origin_position[0] = 15_f32;
3778 delete_count += 1;
3779 deleted = true;
3780 } else {
3781 cr.origin_position[0] += mb.speed;
3782 };
3783 };
3784 };
3785 cr.origin_position[1] = mb.memory_offset + 20_f32;
3786 im1.origin_position = [
3787 cr.origin_position[0] + 5_f32,
3788 cr.origin_position[1] + mb.size[1] / 2_f32,
3789 ];
3790 t1.origin_position = [
3791 im1.origin_position[0] + im1.size[0] + 5_f32,
3792 cr.origin_position[1] + 5_f32,
3793 ];
3794 t2.origin_position = [
3795 im1.origin_position[0] + im1.size[0] + 5_f32,
3796 t1.origin_position[1] + self.get_text_size(&mb.title_name.clone(), ui).unwrap()[1],
3797 ];
3798 im2.origin_position = cr.position;
3799 if !mb.keep_existing
3800 && self.timer.total_time
3801 - self.split_time(&format!("MessageBox{}", mb.name)).unwrap()[1]
3802 >= mb.existing_time
3803 && cr.origin_position[0] == -mb.size[0] - 5_f32
3804 {
3805 mb.exist = false;
3806 if cr.origin_position[0] + mb.speed >= 15_f32 {
3807 cr.origin_position[0] = 15_f32;
3808 } else {
3809 cr.origin_position[0] += mb.speed;
3810 };
3811 };
3812 if let Some(mouse_pos) = ui.input(|i| i.pointer.hover_pos()) {
3813 let rect = egui::Rect::from_min_size(
3814 Pos2 {
3815 x: im2.position[0],
3816 y: im2.position[1],
3817 },
3818 Vec2 {
3819 x: cr.size[0] + 25_f32,
3820 y: cr.size[1] + 25_f32,
3821 },
3822 );
3823 if rect.contains(mouse_pos) {
3824 s.appearance[0].color[3] = 200;
3825 } else {
3826 s.appearance[0].color[3] = 0;
3827 };
3828 };
3829 self.rust_constructor_resource[index_list[i]] = Box::new(mb.clone());
3830 self.replace_resource(&mb.image_name, "Image", im1.clone())
3831 .unwrap();
3832 self.replace_resource(&format!("MessageBox{}", mb.name), "CustomRect", cr.clone())
3833 .unwrap();
3834 self.replace_resource(&mb.title_name, "Text", t1.clone())
3835 .unwrap();
3836 self.replace_resource(&mb.content_name, "Text", t2.clone())
3837 .unwrap();
3838 self.replace_resource(&format!("MessageBox{}Close", mb.name), "Switch", s.clone())
3839 .unwrap();
3840 self.replace_resource(&format!("MessageBox{}Close", mb.name), "Image", im2.clone())
3841 .unwrap();
3842 self.custom_rect(&format!("MessageBox{}", mb.name), ui, ctx)
3843 .unwrap();
3844 self.image(&mb.image_name.clone(), ui, ctx).unwrap();
3845 self.text(&t1.name.clone(), ui, ctx).unwrap();
3846 self.text(&t2.name.clone(), ui, ctx).unwrap();
3847 if self
3848 .switch(
3849 &format!("MessageBox{}Close", mb.name),
3850 ui,
3851 ctx,
3852 s.state == 0 && mb.exist,
3853 true,
3854 )
3855 .unwrap()[0]
3856 == 0
3857 {
3858 mb.exist = false;
3859 if cr.origin_position[0] + mb.speed >= 15_f32 {
3860 cr.origin_position[0] = 15_f32;
3861 } else {
3862 cr.origin_position[0] += mb.speed;
3863 };
3864 self.rust_constructor_resource[index_list[i]] = Box::new(mb.clone());
3865 self.replace_resource(&format!("MessageBox{}", mb.name), "CustomRect", cr.clone())
3866 .unwrap();
3867 };
3868 if deleted {
3869 self.rust_constructor_resource.remove(
3870 self.rust_constructor_resource
3871 .iter()
3872 .position(|x| x.expose_type() == "Image" && x.name() == mb.image_name)
3873 .unwrap(),
3874 );
3875 self.rust_constructor_resource.remove(
3876 self.rust_constructor_resource
3877 .iter()
3878 .position(|x| {
3879 x.expose_type() == "CustomRect"
3880 && x.name() == format!("MessageBox{}", mb.name)
3881 })
3882 .unwrap(),
3883 );
3884 self.rust_constructor_resource.remove(
3885 self.rust_constructor_resource
3886 .iter()
3887 .position(|x| x.expose_type() == "Text" && x.name() == mb.title_name)
3888 .unwrap(),
3889 );
3890 self.rust_constructor_resource.remove(
3891 self.rust_constructor_resource
3892 .iter()
3893 .position(|x| x.expose_type() == "Text" && x.name() == mb.content_name)
3894 .unwrap(),
3895 );
3896 self.rust_constructor_resource.remove(
3897 self.rust_constructor_resource
3898 .iter()
3899 .position(|x| {
3900 x.expose_type() == "Switch"
3901 && x.name() == format!("MessageBox{}Close", mb.name)
3902 })
3903 .unwrap(),
3904 );
3905 self.rust_constructor_resource.remove(
3906 self.rust_constructor_resource
3907 .iter()
3908 .position(|x| {
3909 x.expose_type() == "Image"
3910 && x.name() == format!("MessageBox{}Close", mb.name)
3911 })
3912 .unwrap(),
3913 );
3914 self.rust_constructor_resource.remove(
3915 self.rust_constructor_resource
3916 .iter()
3917 .position(|x| {
3918 x.expose_type() == "SplitTime"
3919 && x.name() == format!("MessageBox{}Animation", mb.name)
3920 })
3921 .unwrap(),
3922 );
3923 if !mb.keep_existing {
3924 self.rust_constructor_resource.remove(
3925 self.rust_constructor_resource
3926 .iter()
3927 .position(|x| {
3928 x.expose_type() == "SplitTime"
3929 && x.name() == format!("MessageBox{}", mb.name)
3930 })
3931 .unwrap(),
3932 );
3933 };
3934 self.rust_constructor_resource.remove(
3935 self.rust_constructor_resource
3936 .iter()
3937 .position(|x| x.expose_type() == "MessageBox" && x.name() == mb.name)
3938 .unwrap(),
3939 );
3940 } else {
3941 offset += mb.size[1] + 15_f32;
3942 };
3943 }
3944 }
3945
3946 pub fn add_switch(
3948 &mut self,
3949 mut switch: Switch,
3950 image_name: &str,
3951 text_name: &str,
3952 ) -> Result<(), RustConstructorError> {
3953 let mut count = 1;
3954 if switch.enable_hover_click_image[0] {
3955 count += 1;
3956 };
3957 if switch.enable_hover_click_image[1] {
3958 count += 1;
3959 };
3960 if switch.appearance.len() < count {
3961 self.problem_report_custom(
3962 RustConstructorError::SwitchAppearanceMismatch {
3963 switch_name: switch.name.clone(),
3964 differ: count as u32 - switch.appearance.len() as u32,
3965 },
3966 SeverityLevel::SevereWarning,
3967 self.problem_list.clone(),
3968 );
3969 return Err(RustConstructorError::SwitchAppearanceMismatch {
3970 switch_name: switch.name.clone(),
3971 differ: count as u32 - switch.appearance.len() as u32,
3972 });
3973 };
3974 for _ in 0..switch.appearance.len() % count {
3975 switch.appearance.pop();
3976 }
3977 if self.check_resource_exists(image_name, "Image") {
3978 let im = self.get_resource_mut::<Image>(image_name, "Image").unwrap();
3979 im.use_overlay_color = true;
3980 switch.switch_image_name = image_name.to_string();
3981 if self.check_resource_exists(text_name, "Text") {
3982 let t = self.get_resource_mut::<Text>(text_name, "Text").unwrap();
3983 switch.text_name = text_name.to_string();
3984 switch.text_origin_position = t.origin_position;
3985 t.center_display = (HorizontalAlign::Center, VerticalAlign::Center);
3986 t.x_grid = [0, 0];
3987 t.y_grid = [0, 0];
3988 } else if !text_name.is_empty() {
3989 self.problem_report_custom(
3990 RustConstructorError::TextNotFound {
3991 text_name: text_name.to_string(),
3992 },
3993 SeverityLevel::SevereWarning,
3994 self.problem_list.clone(),
3995 );
3996 };
3997 } else {
3998 self.problem_report_custom(
3999 RustConstructorError::ImageNotFound {
4000 image_name: image_name.to_string(),
4001 },
4002 SeverityLevel::SevereWarning,
4003 self.problem_list.clone(),
4004 );
4005 return Err(RustConstructorError::ImageNotFound {
4006 image_name: image_name.to_string(),
4007 });
4008 };
4009 switch.hint_text_name = String::new();
4010 if switch
4011 .appearance
4012 .iter()
4013 .filter(|x| !x.hint_text.is_empty())
4014 .count()
4015 > 0
4016 {
4017 switch.hint_text_name = format!("{}Hint", switch.name);
4018 self.add_text(
4019 Text::default()
4020 .name(&format!("{}Hint", switch.name))
4021 .content("")
4022 .origin_position(0_f32, 0_f32)
4023 .font_size(25_f32)
4024 .wrap_width(300_f32)
4025 .background_rounding(10_f32)
4026 .color(255, 255, 255, 0)
4027 .background_color(0, 0, 0, 0)
4028 .center_display(HorizontalAlign::Left, VerticalAlign::Top)
4029 .selectable(false),
4030 );
4031 self.add_split_time(
4032 SplitTime::default().name(&format!("{}StartHoverTime", switch.name)),
4033 );
4034 self.add_split_time(
4035 SplitTime::default().name(&format!("{}HintFadeAnimation", switch.name)),
4036 );
4037 };
4038 switch.state = 0;
4039 switch.animation_count = count as u32;
4040 self.rust_constructor_resource.push(Box::new(switch));
4041 Ok(())
4042 }
4043
4044 pub fn switch(
4046 &mut self,
4047 name: &str,
4048 ui: &mut Ui,
4049 ctx: &egui::Context,
4050 enable: bool,
4051 play_sound: bool,
4052 ) -> Result<[usize; 2], RustConstructorError> {
4053 let mut activated = [5, 0];
4054 let mut appearance_count = 0;
4055 if self.check_resource_exists(name, "Switch") {
4056 let mut s = self.get_resource::<Switch>(name, "Switch").unwrap().clone();
4057 if self.check_resource_exists(&s.switch_image_name.clone(), "Image") {
4058 let mut im = self
4059 .get_resource::<Image>(&s.switch_image_name.clone(), "Image")
4060 .unwrap()
4061 .clone();
4062 s.reg_render_resource(&mut self.render_resource_list);
4063 let rect = Rect::from_min_size(
4064 Pos2::new(im.position[0], im.position[1]),
4065 Vec2::new(im.size[0], im.size[1]),
4066 );
4067 let mut hovered = false;
4068 if enable {
4069 if let Some(mouse_pos) = ui.input(|i| i.pointer.hover_pos()) {
4070 if rect.contains(mouse_pos) {
4072 if !s.hint_text_name.is_empty() {
4073 if self.check_resource_exists(&s.hint_text_name, "Text") {
4074 let mut t = self
4075 .get_resource::<Text>(&s.hint_text_name, "Text")
4076 .unwrap()
4077 .clone();
4078 if !s.last_time_hovered {
4079 self.reset_split_time(&format!("{}StartHoverTime", s.name))
4080 .unwrap();
4081 } else if self.timer.total_time
4082 - self
4083 .split_time(&format!("{}StartHoverTime", s.name))
4084 .unwrap()[1]
4085 >= 2_f32
4086 || t.color[3] != 0
4087 {
4088 t.color[3] = 255;
4089 t.origin_position = [mouse_pos.x, mouse_pos.y];
4090 };
4091 t.center_display.0 = if mouse_pos.x
4092 + self.get_text_size(&s.hint_text_name, ui).unwrap()[0]
4093 <= ctx.available_rect().width()
4094 {
4095 HorizontalAlign::Left
4096 } else {
4097 HorizontalAlign::Right
4098 };
4099 t.center_display.1 = if mouse_pos.y
4100 + self.get_text_size(&s.hint_text_name, ui).unwrap()[1]
4101 <= ctx.available_rect().height()
4102 {
4103 VerticalAlign::Top
4104 } else {
4105 VerticalAlign::Bottom
4106 };
4107 self.replace_resource(&s.hint_text_name, "Text", t.clone())
4108 .unwrap();
4109 } else {
4110 self.problem_report_custom(
4111 RustConstructorError::TextNotFound {
4112 text_name: s.hint_text_name.clone(),
4113 },
4114 SeverityLevel::SevereWarning,
4115 self.problem_list.clone(),
4116 );
4117 return Err(RustConstructorError::TextNotFound {
4118 text_name: s.hint_text_name.clone(),
4119 });
4120 };
4121 };
4122 hovered = true;
4123 let mut clicked = vec![];
4124 let mut active = false;
4125 for u in 0..s.click_method.len() as u32 {
4126 clicked.push(ui.input(|i| {
4127 i.pointer
4128 .button_down(s.click_method[u as usize].click_method)
4129 }));
4130 if clicked[u as usize] {
4131 active = true;
4132 s.last_time_clicked_index = u as usize;
4133 break;
4134 };
4135 }
4136 if active {
4137 s.last_time_clicked = true;
4138 if s.enable_hover_click_image[1] {
4139 if s.enable_hover_click_image[0] {
4140 appearance_count = 2;
4141 } else {
4142 appearance_count = 1;
4143 };
4144 } else if !s.enable_hover_click_image[0] {
4145 appearance_count = 0;
4146 };
4147 } else {
4148 if s.last_time_clicked {
4149 if play_sound {
4150 general_click_feedback(&s.sound_path);
4151 };
4152 let mut count = 1;
4153 if s.enable_hover_click_image[0] {
4154 count += 1;
4155 };
4156 if s.enable_hover_click_image[1] {
4157 count += 1;
4158 };
4159 if s.click_method[s.last_time_clicked_index].action {
4160 if s.state < (s.appearance.len() / count - 1) as u32 {
4161 s.state += 1;
4162 } else {
4163 s.state = 0;
4164 };
4165 };
4166 activated[0] = s.last_time_clicked_index;
4167 s.last_time_clicked = false;
4168 };
4169 if s.enable_hover_click_image[0] {
4170 appearance_count = 1;
4171 } else {
4172 appearance_count = 0;
4173 };
4174 };
4175 } else {
4176 s.last_time_clicked = false;
4177 appearance_count = 0;
4178 };
4179 } else {
4180 s.last_time_clicked = false;
4181 appearance_count = 0;
4182 };
4183 } else {
4184 s.last_time_clicked = false;
4185 appearance_count = 0;
4186 };
4187 if !hovered && !s.hint_text_name.is_empty() {
4188 if s.last_time_hovered {
4189 self.reset_split_time(&format!("{}HintFadeAnimation", s.name))
4190 .unwrap();
4191 };
4192 if self.check_resource_exists(&s.hint_text_name, "Text") {
4193 let mut t = self
4194 .get_resource::<Text>(&s.hint_text_name, "Text")
4195 .unwrap()
4196 .clone();
4197 if self.timer.total_time
4198 - self
4199 .split_time(&format!("{}HintFadeAnimation", s.name))
4200 .unwrap()[1]
4201 >= self.tick_interval
4202 {
4203 self.reset_split_time(&format!("{}HintFadeAnimation", s.name))
4204 .unwrap();
4205 t.color[3] = t.color[3].saturating_sub(10);
4206 };
4207 self.replace_resource(&s.hint_text_name, "Text", t.clone())
4208 .unwrap();
4209 } else {
4210 self.problem_report_custom(
4211 RustConstructorError::TextNotFound {
4212 text_name: s.hint_text_name.clone(),
4213 },
4214 SeverityLevel::SevereWarning,
4215 self.problem_list.clone(),
4216 );
4217 return Err(RustConstructorError::TextNotFound {
4218 text_name: s.hint_text_name.clone(),
4219 });
4220 };
4221 };
4222 im.overlay_color =
4223 s.appearance[(s.state * s.animation_count + appearance_count) as usize].color;
4224 if self.check_resource_exists(
4225 &s.appearance[(s.state * s.animation_count + appearance_count) as usize]
4226 .texture
4227 .clone(),
4228 "ImageTexture",
4229 ) {
4230 im.texture = self
4231 .image_texture(
4232 &s.appearance
4233 [(s.state * s.animation_count + appearance_count) as usize]
4234 .texture
4235 .clone(),
4236 )
4237 .unwrap();
4238 } else {
4239 self.problem_report_custom(
4240 RustConstructorError::ImageTextureNotFound {
4241 image_texture_name: s.appearance
4242 [(s.state * s.animation_count + appearance_count) as usize]
4243 .texture
4244 .clone(),
4245 },
4246 SeverityLevel::SevereWarning,
4247 self.problem_list.clone(),
4248 );
4249 return Err(RustConstructorError::ImageTextureNotFound {
4250 image_texture_name: s.appearance
4251 [(s.state * s.animation_count + appearance_count) as usize]
4252 .texture
4253 .clone(),
4254 });
4255 };
4256 if !s.hint_text_name.is_empty() {
4257 if self.check_resource_exists(&s.hint_text_name, "Text") {
4258 let mut t = self
4259 .get_resource::<Text>(&s.hint_text_name, "Text")
4260 .unwrap()
4261 .clone();
4262 t.background_color[3] = t.color[3];
4263 t.content = s.appearance
4264 [(s.state * s.animation_count + appearance_count) as usize]
4265 .hint_text
4266 .clone();
4267 self.replace_resource(&s.hint_text_name, "Text", t.clone())
4268 .unwrap();
4269 } else {
4270 self.problem_report_custom(
4271 RustConstructorError::TextNotFound {
4272 text_name: s.hint_text_name.clone(),
4273 },
4274 SeverityLevel::SevereWarning,
4275 self.problem_list.clone(),
4276 );
4277 return Err(RustConstructorError::TextNotFound {
4278 text_name: s.hint_text_name,
4279 });
4280 };
4281 };
4282 s.last_time_hovered = hovered;
4283 activated[1] = s.state as usize;
4284 self.replace_resource(name, "Switch", s.clone()).unwrap();
4285 self.replace_resource(&s.switch_image_name, "Image", im.clone())
4286 .unwrap();
4287 self.image(&s.switch_image_name.clone(), ui, ctx).unwrap();
4288 if self.check_resource_exists(&s.text_name, "Text") {
4289 let mut t = self
4290 .get_resource::<Text>(&s.text_name, "Text")
4291 .unwrap()
4292 .clone();
4293 t.origin_position = [
4294 im.position[0] + s.text_origin_position[0],
4295 im.position[1] + s.text_origin_position[1],
4296 ];
4297 t.content = s.appearance
4298 [(s.state * s.animation_count + appearance_count) as usize]
4299 .text
4300 .clone();
4301 self.replace_resource(&s.text_name, "Text", t.clone())
4302 .unwrap();
4303 self.text(&s.text_name, ui, ctx).unwrap();
4304 };
4305 if self.check_resource_exists(&s.hint_text_name, "Text") {
4306 self.text(&s.hint_text_name, ui, ctx).unwrap();
4307 };
4308 Ok(activated)
4309 } else {
4310 self.problem_report_custom(
4311 RustConstructorError::ImageNotFound {
4312 image_name: name.to_string(),
4313 },
4314 SeverityLevel::SevereWarning,
4315 self.problem_list.clone(),
4316 );
4317 Err(RustConstructorError::ImageNotFound {
4318 image_name: s.switch_image_name.clone(),
4319 })
4320 }
4321 } else {
4322 self.problem_report_custom(
4323 RustConstructorError::SwitchNotFound {
4324 switch_name: name.to_string(),
4325 },
4326 SeverityLevel::SevereWarning,
4327 self.problem_list.clone(),
4328 );
4329 Err(RustConstructorError::SwitchNotFound {
4330 switch_name: name.to_string(),
4331 })
4332 }
4333 }
4334}