1use anyhow::Context;
4use eframe::{emath::Rect, epaint::Stroke, epaint::textures::TextureOptions};
5use egui::{
6 Color32, FontData, FontDefinitions, FontId, PointerButton, Pos2, Ui, Vec2, text::CCursor,
7};
8use json::JsonValue;
9use kira::{
10 AudioManager, AudioManagerSettings, DefaultBackend, sound::static_sound::StaticSoundData,
11};
12use std::{
13 collections::HashMap,
14 fs::{self, File},
15 io::Read,
16 path::{Path, PathBuf},
17 sync::Arc,
18 time::Instant,
19 vec::Vec,
20};
21use tray_icon::{Icon, TrayIconBuilder, menu::Menu};
22
23pub fn load_icon_from_file(path: &str) -> Result<Icon, Box<dyn std::error::Error>> {
25 let image = image::open(path)?.into_rgba8();
26 let (width, height) = image.dimensions();
27 let color = image.into_raw();
28 Ok(Icon::from_rgba(color, width, height)?)
29}
30
31pub fn create_json<P: AsRef<Path>>(path: P, data: JsonValue) -> anyhow::Result<()> {
33 let parent_dir = path
34 .as_ref()
35 .parent()
36 .ok_or_else(|| anyhow::anyhow!("Invalid file path."))?;
37
38 fs::create_dir_all(parent_dir)?;
40
41 let formatted = json::stringify_pretty(data, 4);
43
44 fs::write(path, formatted)?;
46 Ok(())
47}
48
49pub fn copy_and_reformat_json<P: AsRef<Path>>(src: P, dest: P) -> anyhow::Result<()> {
51 let content = fs::read_to_string(&src)?;
53
54 let parsed = json::parse(&content)?;
56
57 create_json(dest, parsed)?;
59
60 Ok(())
61}
62
63pub fn check_file_exists<P: AsRef<Path>>(path: P) -> bool {
65 let path_ref = path.as_ref();
66 if path_ref.exists() {
67 true } else {
69 false
71 }
72}
73
74pub fn write_to_json<P: AsRef<Path>>(path: P, data: JsonValue) -> anyhow::Result<()> {
76 let parent_dir = path
77 .as_ref()
78 .parent()
79 .ok_or_else(|| anyhow::anyhow!("Invalid file path."))?;
80
81 fs::create_dir_all(parent_dir)?;
82 let formatted = json::stringify_pretty(data, 4);
83 fs::write(path, formatted)?;
84 Ok(())
85}
86
87pub fn read_from_json<P: AsRef<Path>>(path: P) -> anyhow::Result<JsonValue> {
89 let content = fs::read_to_string(&path)
90 .with_context(|| format!("Cannot read the file: {}", path.as_ref().display()))?;
91 json::parse(&content)
92 .with_context(|| format!("Failed to parse JSON: {}", path.as_ref().display()))
93}
94
95pub fn play_wav(path: &str) -> anyhow::Result<f64> {
97 let mut manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())?;
98 let sound_data = StaticSoundData::from_file(path)?;
99 let duration = sound_data.duration().as_secs_f64();
100 manager.play(sound_data)?;
101 std::thread::sleep(std::time::Duration::from_secs_f64(duration));
102 Ok(duration)
103}
104
105pub fn general_click_feedback(sound_path: &str) {
107 let sound_path = sound_path.to_string();
108 std::thread::spawn(move || {
109 play_wav(&sound_path).unwrap_or(0_f64);
110 });
111}
112
113pub fn count_files_recursive(dir: &Path, target: &str) -> std::io::Result<usize> {
115 let mut count = 0;
116 if dir.is_dir() {
117 for entry in fs::read_dir(dir)? {
118 let entry = entry?;
119 let path = entry.path();
120 if path.is_dir() {
121 count += count_files_recursive(&path, target)?;
122 } else if path.file_name().unwrap().to_string_lossy().contains(target) {
123 count += 1;
124 }
125 }
126 }
127 Ok(count)
128}
129
130pub fn list_files_recursive(path: &Path, prefix: &str) -> Result<Vec<PathBuf>, std::io::Error> {
132 let mut matches = Vec::new();
133
134 for entry in std::fs::read_dir(path)? {
135 let entry = entry?;
137 let path = entry.path();
138
139 if path.is_dir() {
140 matches.extend(list_files_recursive(&path, prefix)?);
142 } else if let Some(file_name) = path.file_name()
143 && file_name.to_string_lossy().contains(prefix)
144 {
145 matches.push(path);
146 }
147 }
148
149 Ok(matches)
150}
151
152#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
154pub struct Config {
155 pub language: u8,
157 pub amount_languages: u8,
159 pub rc_strict_mode: bool,
161 pub problem_report_sound: String,
163}
164
165impl Config {
166 pub fn from_json_value(value: &JsonValue) -> Option<Self> {
167 Some(Self {
168 language: value["language"].as_u8()?,
169 amount_languages: value["amount_languages"].as_u8()?,
170 rc_strict_mode: value["rc_strict_mode"].as_bool()?,
171 problem_report_sound: value["problem_report_sound"].as_str()?.to_string(),
172 })
173 }
174
175 pub fn to_json_value(&self) -> JsonValue {
176 json::object! {
177 language: self.language,
178 amount_languages: self.amount_languages,
179 rc_strict_mode: self.rc_strict_mode,
180 }
181 }
182}
183
184#[derive(Debug, Clone, PartialEq, Eq)]
186pub struct AppText {
187 pub app_text: HashMap<String, Vec<String>>,
188}
189
190impl AppText {
191 pub fn from_json_value(value: &JsonValue) -> Option<Self> {
192 if !value["app_text"].is_object() {
194 return None;
195 }
196
197 let mut parsed = HashMap::new();
199 for (key, val) in value["app_text"].entries() {
200 if let JsonValue::Array(arr) = val {
201 let str_vec: Vec<String> = arr
202 .iter()
203 .filter_map(|v| v.as_str().map(String::from))
204 .collect();
205 parsed.insert(key.to_string(), str_vec);
206 }
207 }
208
209 Some(Self { app_text: parsed })
210 }
211}
212
213#[derive(Debug, Clone, PartialEq, PartialOrd)]
215pub enum Value {
216 Bool(bool),
217 Int(i32),
218 UInt(u32),
219 Float(f32),
220 Vec(Vec<Value>),
221 String(String),
222}
223
224impl From<bool> for Value {
225 fn from(b: bool) -> Self {
226 Value::Bool(b)
227 }
228}
229
230impl From<i32> for Value {
231 fn from(i: i32) -> Self {
232 Value::Int(i)
233 }
234}
235
236impl From<u32> for Value {
237 fn from(u: u32) -> Self {
238 Value::UInt(u)
239 }
240}
241
242impl From<f32> for Value {
243 fn from(f: f32) -> Self {
244 Value::Float(f)
245 }
246}
247
248impl<T: Into<Value>> From<Vec<T>> for Value {
249 fn from(v: Vec<T>) -> Self {
250 Value::Vec(v.into_iter().map(|x| x.into()).collect())
251 }
252}
253
254impl From<String> for Value {
255 fn from(s: String) -> Self {
256 Value::String(s)
257 }
258}
259
260#[derive(Debug, Clone, PartialEq, PartialOrd)]
262pub struct ReportState {
263 pub current_page: String,
265 pub current_total_runtime: f32,
267 pub current_page_runtime: f32,
269}
270
271#[derive(Debug, Clone, PartialEq, PartialOrd)]
273pub struct Problem {
274 pub severity_level: SeverityLevel,
276 pub problem: String,
278 pub annotation: String,
280 pub report_state: ReportState,
282 pub problem_type: RustConstructorError,
284}
285
286#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
288pub enum SeverityLevel {
289 MildWarning,
291 SevereWarning,
293 Error,
295}
296
297pub trait RustConstructorResource {
299 fn name(&self) -> &str;
301
302 fn expose_type(&self) -> &str;
304
305 fn reg_render_resource(&self, render_list: &mut Vec<RenderResource>);
307
308 fn match_resource(&self, resource_name: &str, resource_type: &str) -> bool {
310 resource_name == self.name() && resource_type == self.expose_type()
311 }
312}
313
314impl RustConstructorResource for PageData {
315 fn name(&self) -> &str {
316 &self.name
317 }
318
319 fn expose_type(&self) -> &str {
320 &self.discern_type
321 }
322
323 fn reg_render_resource(&self, render_list: &mut Vec<RenderResource>) {
324 render_list.push(RenderResource {
325 discern_type: self.expose_type().to_string(),
326 name: self.name.to_string(),
327 });
328 }
329}
330
331#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
333pub struct PageData {
334 pub discern_type: String,
335 pub name: String,
336 pub forced_update: bool,
338 pub change_page_updated: bool,
340 pub enter_page_updated: bool,
342}
343
344impl Default for PageData {
345 fn default() -> Self {
346 PageData {
347 discern_type: String::from("PageData"),
348 name: String::from("PageData"),
349 forced_update: true,
350 change_page_updated: false,
351 enter_page_updated: false,
352 }
353 }
354}
355
356impl PageData {
357 #[inline]
358 pub fn name(mut self, name: &str) -> Self {
359 self.name = name.to_string();
360 self
361 }
362
363 #[inline]
364 pub fn forced_update(mut self, forced_update: bool) -> Self {
365 self.forced_update = forced_update;
366 self
367 }
368}
369
370#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
372pub struct Timer {
373 pub start_time: f32,
375 pub total_time: f32,
377 pub timer: Instant,
379 pub now_time: f32,
381}
382
383impl RustConstructorResource for ImageTexture {
384 fn name(&self) -> &str {
385 &self.name
386 }
387
388 fn expose_type(&self) -> &str {
389 &self.discern_type
390 }
391
392 fn reg_render_resource(&self, render_list: &mut Vec<RenderResource>) {
393 render_list.push(RenderResource {
394 discern_type: self.expose_type().to_string(),
395 name: self.name.to_string(),
396 });
397 }
398}
399
400#[derive(Clone, PartialEq, Eq, Hash)]
402pub struct ImageTexture {
403 pub discern_type: String,
404 pub name: String,
405 pub texture: Option<egui::TextureHandle>,
407 pub cite_path: String,
409}
410
411impl Default for ImageTexture {
412 fn default() -> Self {
413 ImageTexture {
414 discern_type: String::from("ImageTexture"),
415 name: String::from("ImageTexture"),
416 texture: None,
417 cite_path: String::from(""),
418 }
419 }
420}
421
422impl ImageTexture {
423 #[inline]
424 pub fn name(mut self, name: &str) -> Self {
425 self.name = name.to_string();
426 self
427 }
428}
429
430impl RustConstructorResource for CustomRect {
431 fn name(&self) -> &str {
432 &self.name
433 }
434
435 fn expose_type(&self) -> &str {
436 &self.discern_type
437 }
438
439 fn reg_render_resource(&self, render_list: &mut Vec<RenderResource>) {
440 render_list.push(RenderResource {
441 discern_type: self.expose_type().to_string(),
442 name: self.name.to_string(),
443 });
444 }
445}
446
447#[derive(Debug, Clone, PartialEq, PartialOrd)]
449pub struct CustomRect {
450 pub discern_type: String,
451 pub name: String,
452 pub position: [f32; 2],
454 pub size: [f32; 2],
456 pub rounding: f32,
458 pub x_grid: [u32; 2],
460 pub y_grid: [u32; 2],
462 pub center_display: (HorizontalAlign, VerticalAlign),
464 pub color: [u8; 4],
466 pub border_width: f32,
468 pub border_color: [u8; 4],
470 pub origin_position: [f32; 2],
472}
473
474impl Default for CustomRect {
475 fn default() -> Self {
476 Self {
477 discern_type: String::from("CustomRect"),
478 name: String::from("CustomRect"),
479 position: [0_f32, 0_f32],
480 size: [100_f32, 100_f32],
481 rounding: 2_f32,
482 x_grid: [0, 0],
483 y_grid: [0, 0],
484 center_display: (HorizontalAlign::default(), VerticalAlign::default()),
485 color: [255, 255, 255, 255],
486 border_width: 2_f32,
487 border_color: [0, 0, 0, 255],
488 origin_position: [0_f32, 0_f32],
489 }
490 }
491}
492
493impl CustomRect {
494 #[inline]
495 pub fn name(mut self, name: &str) -> Self {
496 self.name = name.to_string();
497 self
498 }
499
500 #[inline]
501 pub fn size(mut self, width: f32, height: f32) -> Self {
502 self.size = [width, height];
503 self
504 }
505
506 #[inline]
507 pub fn rounding(mut self, rounding: f32) -> Self {
508 self.rounding = rounding;
509 self
510 }
511
512 #[inline]
513 pub fn x_grid(mut self, fetch: u32, total: u32) -> Self {
514 self.x_grid = [fetch, total];
515 self
516 }
517
518 #[inline]
519 pub fn y_grid(mut self, fetch: u32, total: u32) -> Self {
520 self.y_grid = [fetch, total];
521 self
522 }
523
524 #[inline]
525 pub fn center_display(
526 mut self,
527 horizontal_align: HorizontalAlign,
528 vertical_align: VerticalAlign,
529 ) -> Self {
530 self.center_display = (horizontal_align, vertical_align);
531 self
532 }
533
534 #[inline]
535 pub fn color(mut self, r: u8, g: u8, b: u8, a: u8) -> Self {
536 self.color = [r, g, b, a];
537 self
538 }
539
540 #[inline]
541 pub fn border_width(mut self, border_width: f32) -> Self {
542 self.border_width = border_width;
543 self
544 }
545
546 #[inline]
547 pub fn border_color(mut self, r: u8, g: u8, b: u8, a: u8) -> Self {
548 self.border_color = [r, g, b, a];
549 self
550 }
551
552 #[inline]
553 pub fn origin_position(mut self, x: f32, y: f32) -> Self {
554 self.origin_position = [x, y];
555 self
556 }
557}
558
559impl RustConstructorResource for Image {
560 fn name(&self) -> &str {
561 &self.name
562 }
563
564 fn expose_type(&self) -> &str {
565 &self.discern_type
566 }
567
568 fn reg_render_resource(&self, render_list: &mut Vec<RenderResource>) {
569 render_list.push(RenderResource {
570 discern_type: self.expose_type().to_string(),
571 name: self.name.to_string(),
572 });
573 }
574}
575
576#[derive(Clone, PartialEq)]
578pub struct Image {
579 pub discern_type: String,
580 pub name: String,
581 pub texture: Option<egui::TextureHandle>,
583 pub position: [f32; 2],
585 pub size: [f32; 2],
587 pub x_grid: [u32; 2],
589 pub y_grid: [u32; 2],
591 pub center_display: (HorizontalAlign, VerticalAlign),
593 pub alpha: u8,
595 pub overlay_color: [u8; 4],
597 pub use_overlay_color: bool,
599 pub origin_position: [f32; 2],
601 pub cite_texture: String,
603 pub last_frame_cite_texture: String,
605}
606
607impl Default for Image {
608 fn default() -> Self {
609 Self {
610 discern_type: String::from("Image"),
611 name: String::from("Image"),
612 texture: None,
613 position: [0_f32, 0_f32],
614 size: [100_f32, 100_f32],
615 x_grid: [0, 0],
616 y_grid: [0, 0],
617 center_display: (HorizontalAlign::default(), VerticalAlign::default()),
618 alpha: 255,
619 overlay_color: [255, 255, 255, 255],
620 use_overlay_color: true,
621 origin_position: [0_f32, 0_f32],
622 cite_texture: String::from("ImageTexture"),
623 last_frame_cite_texture: String::from("ImageTexture"),
624 }
625 }
626}
627
628impl Image {
629 #[inline]
630 pub fn name(mut self, name: &str) -> Self {
631 self.name = name.to_string();
632 self
633 }
634
635 #[inline]
636 pub fn origin_position(mut self, x: f32, y: f32) -> Self {
637 self.origin_position = [x, y];
638 self
639 }
640
641 #[inline]
642 pub fn size(mut self, width: f32, height: f32) -> Self {
643 self.size = [width, height];
644 self
645 }
646
647 #[inline]
648 pub fn x_grid(mut self, fetch: u32, total: u32) -> Self {
649 self.x_grid = [fetch, total];
650 self
651 }
652
653 #[inline]
654 pub fn y_grid(mut self, fetch: u32, total: u32) -> Self {
655 self.y_grid = [fetch, total];
656 self
657 }
658
659 #[inline]
660 pub fn center_display(
661 mut self,
662 horizontal_align: HorizontalAlign,
663 vertical_align: VerticalAlign,
664 ) -> Self {
665 self.center_display = (horizontal_align, vertical_align);
666 self
667 }
668
669 #[inline]
670 pub fn alpha(mut self, alpha: u8) -> Self {
671 self.alpha = alpha;
672 self
673 }
674
675 #[inline]
676 pub fn overlay_color(mut self, r: u8, g: u8, b: u8, a: u8) -> Self {
677 self.overlay_color = [r, g, b, a];
678 self
679 }
680
681 #[inline]
682 pub fn use_overlay_color(mut self, use_overlay_color: bool) -> Self {
683 self.use_overlay_color = use_overlay_color;
684 self
685 }
686}
687
688impl RustConstructorResource for Text {
689 fn name(&self) -> &str {
690 &self.name
691 }
692
693 fn expose_type(&self) -> &str {
694 &self.discern_type
695 }
696
697 fn reg_render_resource(&self, render_list: &mut Vec<RenderResource>) {
698 render_list.push(RenderResource {
699 discern_type: self.expose_type().to_string(),
700 name: self.name.to_string(),
701 });
702 }
703}
704
705#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
707pub enum HyperlinkSelectMethod {
708 All(String),
710 Segment(Vec<(usize, String)>),
712}
713
714#[derive(Debug, Clone, PartialEq, PartialOrd)]
716pub struct Text {
717 pub discern_type: String,
718 pub name: String,
719 pub content: String,
721 pub font_size: f32,
723 pub color: [u8; 4],
725 pub position: [f32; 2],
727 pub center_display: (HorizontalAlign, VerticalAlign),
729 pub wrap_width: f32,
731 pub write_background: bool,
733 pub background_color: [u8; 4],
735 pub background_rounding: f32,
737 pub x_grid: [u32; 2],
739 pub y_grid: [u32; 2],
741 pub origin_position: [f32; 2],
743 pub font: String,
745 pub selection: Option<(usize, usize)>,
747 pub selectable: bool,
749 pub hyperlink_text: Vec<(String, HyperlinkSelectMethod)>,
751 pub hyperlink_index: Vec<(usize, usize, String)>,
753 pub last_frame_content: String,
755}
756
757impl Default for Text {
758 fn default() -> Self {
759 Self {
760 discern_type: String::from("Text"),
761 name: String::from("Text"),
762 content: String::from("Hello world"),
763 font_size: 16_f32,
764 color: [255, 255, 255, 255],
765 position: [0_f32, 0_f32],
766 center_display: (HorizontalAlign::default(), VerticalAlign::default()),
767 wrap_width: 200_f32,
768 write_background: true,
769 background_color: [0, 0, 0, 255],
770 background_rounding: 2_f32,
771 x_grid: [0, 0],
772 y_grid: [0, 0],
773 origin_position: [0_f32, 0_f32],
774 font: String::new(),
775 selection: None,
776 selectable: true,
777 hyperlink_text: Vec::new(),
778 hyperlink_index: Vec::new(),
779 last_frame_content: String::from("Hello world"),
780 }
781 }
782}
783
784impl Text {
785 #[inline]
786 pub fn name(mut self, name: &str) -> Self {
787 self.name = name.to_string();
788 self
789 }
790
791 #[inline]
792 pub fn content(mut self, content: &str) -> Self {
793 self.content = content.to_string();
794 self
795 }
796
797 #[inline]
798 pub fn font_size(mut self, font_size: f32) -> Self {
799 self.font_size = font_size;
800 self
801 }
802
803 #[inline]
804 pub fn color(mut self, r: u8, g: u8, b: u8, a: u8) -> Self {
805 self.color = [r, g, b, a];
806 self
807 }
808
809 #[inline]
810 pub fn center_display(
811 mut self,
812 horizontal_align: HorizontalAlign,
813 vertical_align: VerticalAlign,
814 ) -> Self {
815 self.center_display = (horizontal_align, vertical_align);
816 self
817 }
818
819 #[inline]
820 pub fn wrap_width(mut self, wrap_width: f32) -> Self {
821 self.wrap_width = wrap_width;
822 self
823 }
824
825 #[inline]
826 pub fn write_background(mut self, write_background: bool) -> Self {
827 self.write_background = write_background;
828 self
829 }
830
831 #[inline]
832 pub fn background_color(mut self, r: u8, g: u8, b: u8, a: u8) -> Self {
833 self.background_color = [r, g, b, a];
834 self
835 }
836
837 #[inline]
838 pub fn background_rounding(mut self, background_rounding: f32) -> Self {
839 self.background_rounding = background_rounding;
840 self
841 }
842
843 #[inline]
844 pub fn x_grid(mut self, fetch: u32, total: u32) -> Self {
845 self.x_grid = [fetch, total];
846 self
847 }
848
849 #[inline]
850 pub fn y_grid(mut self, fetch: u32, total: u32) -> Self {
851 self.y_grid = [fetch, total];
852 self
853 }
854
855 #[inline]
856 pub fn origin_position(mut self, x: f32, y: f32) -> Self {
857 self.origin_position = [x, y];
858 self
859 }
860
861 #[inline]
862 pub fn font(mut self, font: &str) -> Self {
863 self.font = font.to_string();
864 self
865 }
866
867 #[inline]
868 pub fn selectable(mut self, selectable: bool) -> Self {
869 self.selectable = selectable;
870 self
871 }
872
873 #[inline]
874 pub fn hyperlink_text(
875 mut self,
876 target_text: &str,
877 select_method: HyperlinkSelectMethod,
878 ) -> Self {
879 self.hyperlink_text
880 .push((target_text.to_string(), select_method));
881 self
882 }
883}
884
885impl RustConstructorResource for Variable {
886 fn name(&self) -> &str {
887 &self.name
888 }
889
890 fn expose_type(&self) -> &str {
891 &self.discern_type
892 }
893
894 fn reg_render_resource(&self, render_list: &mut Vec<RenderResource>) {
895 render_list.push(RenderResource {
896 discern_type: self.expose_type().to_string(),
897 name: self.name.to_string(),
898 });
899 }
900}
901
902#[derive(Debug, Clone, PartialEq, PartialOrd)]
904pub struct Variable {
905 pub discern_type: String,
906 pub name: String,
907 pub value: Value,
909}
910
911impl Default for Variable {
912 fn default() -> Self {
913 Variable {
914 discern_type: String::from("Variable"),
915 name: String::from("Variable"),
916 value: Value::String(String::from("Hello world")),
917 }
918 }
919}
920
921impl Variable {
922 pub fn new<T: Into<Value>>(self, name: &str, value: T) -> Self {
923 Self {
924 discern_type: String::from("Variable"),
925 name: String::from(name),
926 value: value.into(),
927 }
928 }
929
930 pub fn from_bool(self, name: &str, value: bool) -> Self {
931 Self {
932 name: String::from(name),
933 value: Value::Bool(value),
934 ..self
935 }
936 }
937
938 pub fn from_int(self, name: &str, value: i32) -> Self {
939 Self {
940 name: String::from(name),
941 value: Value::Int(value),
942 ..self
943 }
944 }
945
946 pub fn from_uint(self, name: &str, value: u32) -> Self {
947 Self {
948 name: String::from(name),
949 value: Value::UInt(value),
950 ..self
951 }
952 }
953
954 pub fn from_float(self, name: &str, value: f32) -> Self {
955 Self {
956 name: String::from(name),
957 value: Value::Float(value),
958 ..self
959 }
960 }
961
962 pub fn from_vec(self, name: &str, value: Vec<Value>) -> Self {
963 Self {
964 name: String::from(name),
965 value: Value::Vec(value),
966 ..self
967 }
968 }
969
970 pub fn from_string<T: Into<String>>(self, name: &str, value: T) -> Self {
971 Self {
972 name: String::from(name),
973 value: Value::String(value.into()),
974 ..self
975 }
976 }
977}
978
979#[derive(Debug, Clone, PartialEq)]
981pub struct Font {
982 pub name: String,
983 pub discern_type: String,
984 pub font_definitions: FontDefinitions,
986 pub path: String,
988}
989
990impl Default for Font {
991 fn default() -> Self {
992 Self {
993 discern_type: String::from("Font"),
994 name: String::from("Font"),
995 font_definitions: FontDefinitions::default(),
996 path: String::from(""),
997 }
998 }
999}
1000
1001impl Font {
1002 #[inline]
1003 pub fn name(mut self, name: &str) -> Self {
1004 self.name = name.to_string();
1005 self
1006 }
1007
1008 #[inline]
1009 pub fn path(mut self, path: &str) -> Self {
1010 self.path = path.to_string();
1011 self
1012 }
1013}
1014
1015impl RustConstructorResource for Font {
1016 fn name(&self) -> &str {
1017 &self.name
1018 }
1019
1020 fn expose_type(&self) -> &str {
1021 &self.discern_type
1022 }
1023
1024 fn reg_render_resource(&self, render_list: &mut Vec<RenderResource>) {
1025 render_list.push(RenderResource {
1026 discern_type: self.expose_type().to_string(),
1027 name: self.name.to_string(),
1028 });
1029 }
1030}
1031
1032impl RustConstructorResource for SplitTime {
1033 fn name(&self) -> &str {
1034 &self.name
1035 }
1036
1037 fn expose_type(&self) -> &str {
1038 &self.discern_type
1039 }
1040
1041 fn reg_render_resource(&self, render_list: &mut Vec<RenderResource>) {
1042 render_list.push(RenderResource {
1043 discern_type: self.expose_type().to_string(),
1044 name: self.name.to_string(),
1045 });
1046 }
1047}
1048
1049#[derive(Debug, Clone, PartialEq, PartialOrd)]
1051pub struct SplitTime {
1052 pub discern_type: String,
1053 pub name: String,
1054 pub time: [f32; 2],
1056}
1057
1058impl Default for SplitTime {
1059 fn default() -> Self {
1060 Self {
1061 discern_type: String::from("SplitTime"),
1062 name: String::from("SplitTime"),
1063 time: [0_f32, 0_f32],
1064 }
1065 }
1066}
1067
1068impl SplitTime {
1069 #[inline]
1070 pub fn name(mut self, name: &str) -> Self {
1071 self.name = name.to_string();
1072 self
1073 }
1074}
1075
1076impl RustConstructorResource for Switch {
1077 fn name(&self) -> &str {
1078 &self.name
1079 }
1080
1081 fn expose_type(&self) -> &str {
1082 &self.discern_type
1083 }
1084
1085 fn reg_render_resource(&self, render_list: &mut Vec<RenderResource>) {
1086 render_list.push(RenderResource {
1087 discern_type: self.expose_type().to_string(),
1088 name: self.name.to_string(),
1089 });
1090 }
1091}
1092
1093#[derive(Debug, Clone, PartialEq)]
1095pub struct Switch {
1096 pub discern_type: String,
1097 pub name: String,
1098 pub appearance: Vec<SwitchData>,
1100 pub switch_image_name: String,
1102 pub enable_hover_click_image: [bool; 2],
1104 pub state: u32,
1106 pub click_method: Vec<SwitchClickAction>,
1108 pub last_time_hovered: bool,
1110 pub last_time_clicked: bool,
1112 pub last_time_clicked_index: usize,
1114 pub animation_count: u32,
1116 pub hint_text_name: String,
1118 pub text_name: String,
1120 pub text_origin_position: [f32; 2],
1122 pub sound_path: String,
1124}
1125
1126impl Default for Switch {
1127 fn default() -> Self {
1128 Self {
1129 discern_type: String::from("Switch"),
1130 name: String::from("Switch"),
1131 appearance: vec![],
1132 switch_image_name: String::from("Image"),
1133 enable_hover_click_image: [false, false],
1134 state: 0,
1135 click_method: vec![],
1136 last_time_hovered: false,
1137 last_time_clicked: false,
1138 last_time_clicked_index: 0,
1139 animation_count: 0,
1140 hint_text_name: String::from("HintText"),
1141 text_name: String::from("Text"),
1142 text_origin_position: [0_f32, 0_f32],
1143 sound_path: String::from(""),
1144 }
1145 }
1146}
1147
1148impl Switch {
1149 #[inline]
1150 pub fn name(mut self, name: &str) -> Self {
1151 self.name = name.to_string();
1152 self
1153 }
1154
1155 #[inline]
1156 pub fn appearance(mut self, appearance: Vec<SwitchData>) -> Self {
1157 self.appearance = appearance;
1158 self
1159 }
1160
1161 #[inline]
1162 pub fn enable_hover_click_image(
1163 mut self,
1164 enable_hover_image: bool,
1165 enable_click_image: bool,
1166 ) -> Self {
1167 self.enable_hover_click_image = [enable_hover_image, enable_click_image];
1168 self
1169 }
1170
1171 #[inline]
1172 pub fn click_method(mut self, click_method: Vec<SwitchClickAction>) -> Self {
1173 self.click_method = click_method;
1174 self
1175 }
1176
1177 #[inline]
1178 pub fn sound_path(mut self, sound_path: &str) -> Self {
1179 self.sound_path = sound_path.to_string();
1180 self
1181 }
1182}
1183
1184#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1186pub struct RenderResource {
1187 pub discern_type: String,
1188 pub name: String,
1189}
1190
1191#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1193pub struct SwitchData {
1194 pub texture: String,
1196 pub color: [u8; 4],
1198 pub text: String,
1200 pub hint_text: String,
1202}
1203
1204#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1206pub struct SwitchClickAction {
1207 pub click_method: PointerButton,
1209 pub action: bool,
1211}
1212
1213#[derive(Debug, Clone, PartialEq, PartialOrd)]
1215pub struct MessageBox {
1216 pub discern_type: String,
1217 pub name: String,
1218 pub size: [f32; 2],
1220 pub content_name: String,
1222 pub title_name: String,
1224 pub image_name: String,
1226 pub keep_existing: bool,
1228 pub existing_time: f32,
1230 pub exist: bool,
1232 pub speed: f32,
1234 pub restore_speed: f32,
1236 pub memory_offset: f32,
1238}
1239
1240impl Default for MessageBox {
1241 fn default() -> Self {
1242 Self {
1243 discern_type: String::from("MessageBox"),
1244 name: String::from("MessageBox"),
1245 size: [100_f32, 100_f32],
1246 content_name: String::from("Content"),
1247 title_name: String::from("Title"),
1248 image_name: String::from("Image"),
1249 keep_existing: false,
1250 existing_time: 3_f32,
1251 exist: true,
1252 speed: 30_f32,
1253 restore_speed: 10_f32,
1254 memory_offset: 0_f32,
1255 }
1256 }
1257}
1258
1259impl MessageBox {
1260 #[inline]
1261 pub fn name(mut self, name: &str) -> Self {
1262 self.name = name.to_string();
1263 self
1264 }
1265
1266 #[inline]
1267 pub fn size(mut self, width: f32, height: f32) -> Self {
1268 self.size = [width, height];
1269 self
1270 }
1271
1272 #[inline]
1273 pub fn keep_existing(mut self, keep_existing: bool) -> Self {
1274 self.keep_existing = keep_existing;
1275 self
1276 }
1277
1278 #[inline]
1279 pub fn existing_time(mut self, existing_time: f32) -> Self {
1280 self.existing_time = existing_time;
1281 self
1282 }
1283
1284 #[inline]
1285 pub fn speed(mut self, speed: f32) -> Self {
1286 self.speed = speed;
1287 self
1288 }
1289
1290 #[inline]
1291 pub fn restore_speed(mut self, restore_speed: f32) -> Self {
1292 self.restore_speed = restore_speed;
1293 self
1294 }
1295}
1296
1297impl RustConstructorResource for MessageBox {
1298 fn name(&self) -> &str {
1299 &self.name
1300 }
1301
1302 fn expose_type(&self) -> &str {
1303 &self.discern_type
1304 }
1305
1306 fn reg_render_resource(&self, render_list: &mut Vec<RenderResource>) {
1307 render_list.push(RenderResource {
1308 discern_type: self.expose_type().to_string(),
1309 name: self.name.to_string(),
1310 });
1311 }
1312}
1313
1314#[derive(Clone)]
1316pub enum RCR {
1317 Image(Image),
1319 Text(Text),
1321 CustomRect(CustomRect),
1323 Variable(Variable),
1325 Font(Font),
1327 SplitTime(SplitTime),
1329 Switch(Switch),
1331 MessageBox(MessageBox),
1333 ImageTexture(ImageTexture),
1335 PageData(PageData),
1337}
1338
1339#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1341pub enum RustConstructorError {
1342 ImageGetFailed { image_path: String },
1344 ImageNotFound { image_name: String },
1346 TextNotFound { text_name: String },
1348 VariableNotFound { variable_name: String },
1350 VariableNotInt { variable_name: String },
1352 VariableNotUInt { variable_name: String },
1354 VariableNotFloat { variable_name: String },
1356 VariableNotVec { variable_name: String },
1358 VariableNotBool { variable_name: String },
1360 VariableNotString { variable_name: String },
1362 SplitTimeNotFound { split_time_name: String },
1364 SwitchAppearanceMismatch { switch_name: String, differ: u32 },
1366 SwitchNotFound { switch_name: String },
1368 MessageBoxAlreadyExists { message_box_name: String },
1370 FontGetFailed { font_path: String },
1372 FontNotFound { font_name: String },
1374 ResourceNotFound {
1376 resource_name: String,
1377 resource_type: String,
1378 },
1379 PageNotFound { page_name: String },
1381}
1382
1383impl Default for RustConstructorError {
1384 fn default() -> Self {
1385 RustConstructorError::ImageGetFailed {
1386 image_path: "".to_string(),
1387 }
1388 }
1389}
1390
1391#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1393pub enum HorizontalAlign {
1394 #[default]
1396 Left,
1397 Center,
1399 Right,
1401}
1402
1403#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1405pub enum VerticalAlign {
1406 #[default]
1408 Top,
1409 Center,
1411 Bottom,
1413}
1414
1415#[derive(Clone)]
1417pub struct App {
1418 pub config: Config,
1420 pub app_text: AppText,
1422 pub rust_constructor_resource: Vec<RCR>,
1424 pub render_resource_list: Vec<RenderResource>,
1426 pub problem_list: Vec<Problem>,
1428 pub vertrefresh: f32,
1430 pub page: String,
1432 pub timer: Timer,
1434 pub frame_times: Vec<f32>,
1436 pub last_frame_time: Option<f64>,
1438 pub tray_icon: Option<tray_icon::TrayIcon>,
1440 pub tray_icon_created: bool,
1442}
1443
1444impl App {
1445 pub fn new(config: Config, app_text: AppText) -> Self {
1447 Self {
1448 config,
1449 app_text,
1450 rust_constructor_resource: vec![],
1451 render_resource_list: Vec::new(),
1452 problem_list: Vec::new(),
1453 vertrefresh: 0.01,
1454 page: "Launch".to_string(),
1455 timer: Timer {
1456 start_time: 0.0,
1457 total_time: 0.0,
1458 timer: Instant::now(),
1459 now_time: 0.0,
1460 },
1461 frame_times: Vec::new(),
1462 last_frame_time: None,
1463 tray_icon: None,
1464 tray_icon_created: false,
1465 }
1466 }
1467
1468 pub fn page_handler(&mut self, ctx: &egui::Context) {
1470 self.update_frame_stats(ctx);
1472 self.render_resource_list = Vec::new();
1474 self.update_timer();
1476 if let Ok(id) = self.get_resource_index("PageData", &self.page.clone())
1477 && let RCR::PageData(pd) = self.rust_constructor_resource[id].clone()
1478 && pd.forced_update
1479 {
1480 ctx.request_repaint();
1482 };
1483 }
1484
1485 pub fn add_page(&mut self, mut page_data: PageData) {
1487 page_data.change_page_updated = false;
1488 page_data.enter_page_updated = false;
1489 self.rust_constructor_resource
1490 .push(RCR::PageData(page_data));
1491 }
1492
1493 pub fn switch_page(&mut self, page: &str) {
1495 if let Ok(id) = self.get_resource_index("PageData", page) {
1496 self.page = page.to_string();
1497 if let RCR::PageData(pd) = &mut self.rust_constructor_resource[id] {
1498 pd.enter_page_updated = false;
1499 self.timer.start_time = self.timer.total_time;
1500 self.update_timer();
1501 };
1502 };
1503 }
1504
1505 pub fn tray_icon_init(&mut self, icon_path: &str, tooltip: &str, menu: Box<Menu>) {
1507 let icon = load_icon_from_file(icon_path).unwrap();
1508 if let Ok(tray_icon) = TrayIconBuilder::new()
1509 .with_menu(menu)
1510 .with_tooltip(tooltip)
1511 .with_icon(icon)
1512 .with_icon_as_template(true)
1513 .build()
1514 {
1515 self.tray_icon = Some(tray_icon);
1516 self.tray_icon_created = true;
1517 };
1518 }
1519
1520 pub fn check_resource_exists(&mut self, resource_type: &str, resource_name: &str) -> bool {
1522 for i in 0..self.rust_constructor_resource.len() {
1523 match self.rust_constructor_resource[i].clone() {
1524 RCR::Image(im) => {
1525 if im.match_resource(resource_name, resource_type) {
1526 return true;
1527 }
1528 }
1529 RCR::Text(t) => {
1530 if t.match_resource(resource_name, resource_type) {
1531 return true;
1532 }
1533 }
1534 RCR::CustomRect(cr) => {
1535 if cr.match_resource(resource_name, resource_type) {
1536 return true;
1537 }
1538 }
1539 RCR::Variable(v) => {
1540 if v.match_resource(resource_name, resource_type) {
1541 return true;
1542 }
1543 }
1544 RCR::Font(f) => {
1545 if f.match_resource(resource_name, resource_type) {
1546 return true;
1547 }
1548 }
1549 RCR::SplitTime(st) => {
1550 if st.match_resource(resource_name, resource_type) {
1551 return true;
1552 }
1553 }
1554 RCR::Switch(s) => {
1555 if s.match_resource(resource_name, resource_type) {
1556 return true;
1557 }
1558 }
1559 RCR::MessageBox(mb) => {
1560 if mb.match_resource(resource_name, resource_type) {
1561 return true;
1562 }
1563 }
1564 RCR::ImageTexture(it) => {
1565 if it.match_resource(resource_name, resource_type) {
1566 return true;
1567 }
1568 }
1569 RCR::PageData(pd) => {
1570 if pd.match_resource(resource_name, resource_type) {
1571 return true;
1572 }
1573 }
1574 }
1575 }
1576 false
1577 }
1578
1579 pub fn get_resource_index(
1581 &mut self,
1582 resource_type: &str,
1583 resource_name: &str,
1584 ) -> Result<usize, RustConstructorError> {
1585 for i in 0..self.rust_constructor_resource.len() {
1586 match self.rust_constructor_resource[i].clone() {
1587 RCR::Image(im) => {
1588 if im.match_resource(resource_name, resource_type) {
1589 return Ok(i);
1590 }
1591 }
1592 RCR::Text(t) => {
1593 if t.match_resource(resource_name, resource_type) {
1594 return Ok(i);
1595 }
1596 }
1597 RCR::CustomRect(cr) => {
1598 if cr.match_resource(resource_name, resource_type) {
1599 return Ok(i);
1600 }
1601 }
1602 RCR::Variable(v) => {
1603 if v.match_resource(resource_name, resource_type) {
1604 return Ok(i);
1605 }
1606 }
1607 RCR::Font(f) => {
1608 if f.match_resource(resource_name, resource_type) {
1609 return Ok(i);
1610 }
1611 }
1612 RCR::SplitTime(st) => {
1613 if st.match_resource(resource_name, resource_type) {
1614 return Ok(i);
1615 }
1616 }
1617 RCR::Switch(s) => {
1618 if s.match_resource(resource_name, resource_type) {
1619 return Ok(i);
1620 }
1621 }
1622 RCR::MessageBox(mb) => {
1623 if mb.match_resource(resource_name, resource_type) {
1624 return Ok(i);
1625 }
1626 }
1627 RCR::ImageTexture(it) => {
1628 if it.match_resource(resource_name, resource_type) {
1629 return Ok(i);
1630 }
1631 }
1632 RCR::PageData(pd) => {
1633 if pd.match_resource(resource_name, resource_type) {
1634 return Ok(i);
1635 }
1636 }
1637 };
1638 }
1639 self.problem_report(
1640 RustConstructorError::ResourceNotFound {
1641 resource_name: resource_name.to_string(),
1642 resource_type: resource_type.to_string(),
1643 },
1644 SeverityLevel::SevereWarning,
1645 );
1646 Err(RustConstructorError::ResourceNotFound {
1647 resource_name: resource_name.to_string(),
1648 resource_type: resource_type.to_string(),
1649 })
1650 }
1651
1652 pub fn add_fonts(&mut self, mut font: Font) {
1654 let mut fonts = FontDefinitions::default();
1655 if let Ok(font_read_data) = std::fs::read(font.path.clone()) {
1656 let font_data: Arc<Vec<u8>> = Arc::new(font_read_data);
1657 fonts.font_data.insert(
1658 font.name.to_owned(),
1659 Arc::new(FontData::from_owned(
1660 Arc::try_unwrap(font_data).ok().unwrap(),
1661 )),
1662 );
1663
1664 fonts
1666 .families
1667 .entry(egui::FontFamily::Proportional)
1668 .or_default()
1669 .insert(0, font.name.to_owned());
1670
1671 fonts
1672 .families
1673 .entry(egui::FontFamily::Monospace)
1674 .or_default()
1675 .insert(0, font.name.to_owned());
1676
1677 font.font_definitions = fonts;
1678 self.rust_constructor_resource.push(RCR::Font(font));
1679 } else {
1680 self.problem_report(
1681 RustConstructorError::FontGetFailed {
1682 font_path: font.path.to_string(),
1683 },
1684 SeverityLevel::SevereWarning,
1685 );
1686 };
1687 }
1688
1689 pub fn font(&mut self, name: &str) -> Result<FontDefinitions, RustConstructorError> {
1691 if let Ok(id) = self.get_resource_index("Font", name)
1692 && let RCR::Font(f) = &mut self.rust_constructor_resource[id]
1693 {
1694 return Ok(f.font_definitions.clone());
1695 }
1696 self.problem_report(
1697 RustConstructorError::FontNotFound {
1698 font_name: name.to_string(),
1699 },
1700 SeverityLevel::SevereWarning,
1701 );
1702 Err(RustConstructorError::FontNotFound {
1703 font_name: name.to_string(),
1704 })
1705 }
1706
1707 pub fn register_all_fonts(&mut self, ctx: &egui::Context) {
1709 let mut font_definitions = egui::FontDefinitions::default();
1710 let mut font_resources = Vec::new();
1711 for i in 0..self.rust_constructor_resource.len() {
1712 if let RCR::Font(f) = &self.rust_constructor_resource[i] {
1713 font_resources.push(f.clone());
1714 };
1715 }
1716 for i in &font_resources {
1717 let font_name = i.name.clone();
1718 if let Ok(font_def) = self.font(&font_name) {
1720 if let Some(font_data) = font_def.font_data.get(&font_name) {
1722 font_definitions
1723 .font_data
1724 .insert(font_name.clone(), Arc::clone(font_data));
1725 font_definitions
1726 .families
1727 .entry(egui::FontFamily::Name(font_name.clone().into()))
1728 .or_default()
1729 .push(font_name.clone());
1730 };
1731
1732 font_definitions
1734 .families
1735 .entry(egui::FontFamily::Proportional)
1736 .or_default()
1737 .insert(0, font_name.to_owned());
1738
1739 font_definitions
1740 .families
1741 .entry(egui::FontFamily::Monospace)
1742 .or_default()
1743 .insert(0, font_name.to_owned());
1744 };
1745 }
1746 ctx.set_fonts(font_definitions);
1747 }
1748
1749 pub fn problem_report(
1751 &mut self,
1752 problem_type: RustConstructorError,
1753 severity_level: SeverityLevel,
1754 ) {
1755 let (problem, annotation) = match problem_type.clone() {
1756 RustConstructorError::FontGetFailed { font_path } => (
1757 format!("Font get failed: {}", font_path,),
1758 "Please check if the font file exists and the path is correct.",
1759 ),
1760 RustConstructorError::FontNotFound { font_name } => (
1761 format!("Font not found: {}", font_name,),
1762 "Please check whether the font has been added.",
1763 ),
1764 RustConstructorError::ImageGetFailed { image_path } => (
1765 format!("Image get failed: {}", image_path,),
1766 "Please check whether the image path is correct and whether the image has been added.",
1767 ),
1768 RustConstructorError::ImageNotFound { image_name } => (
1769 format!("Image not found: {}", image_name,),
1770 "Please check whether the image has been added.",
1771 ),
1772 RustConstructorError::TextNotFound { text_name } => (
1773 format!("Text not found: {}", text_name,),
1774 "Please check whether the text has been added.",
1775 ),
1776 RustConstructorError::MessageBoxAlreadyExists { message_box_name } => (
1777 format!("Message box already exists: {}", message_box_name),
1778 "Please check whether the code for generating the message box has been accidentally called multiple times.",
1779 ),
1780 RustConstructorError::SplitTimeNotFound { split_time_name } => (
1781 format!("Split time not found: {}", split_time_name,),
1782 "Please check whether the split time has been added.",
1783 ),
1784 RustConstructorError::SwitchAppearanceMismatch {
1785 switch_name,
1786 differ,
1787 } => (
1788 format!(
1789 "Switch appearance list's number of items is large / small {} more: {}",
1790 differ, switch_name
1791 ),
1792 "Please check whether the number of appearance list items matches the number of enabled animations.",
1793 ),
1794 RustConstructorError::SwitchNotFound { switch_name } => (
1795 format!("Switch not found: {}", switch_name,),
1796 "Please check whether the switch has been added.",
1797 ),
1798 RustConstructorError::PageNotFound { page_name } => (
1799 format!("Page not found: {}", page_name,),
1800 "Please check whether the page has been added.",
1801 ),
1802 RustConstructorError::VariableNotFound { variable_name } => (
1803 format!("Variable not found: {}", variable_name,),
1804 "Please check whether the variable has been added.",
1805 ),
1806 RustConstructorError::VariableNotBool { variable_name } => (
1807 format!("Variable is not bool: {}", variable_name,),
1808 "Please check whether the variable names and types are correct and whether there are duplicate items.",
1809 ),
1810 RustConstructorError::VariableNotFloat { variable_name } => (
1811 format!("Variable is not f32: {}", variable_name,),
1812 "Please check whether the variable names and types are correct and whether there are duplicate items.",
1813 ),
1814 RustConstructorError::VariableNotInt { variable_name } => (
1815 format!("Variable is not int: {}", variable_name,),
1816 "Please check whether the variable names and types are correct and whether there are duplicate items.",
1817 ),
1818 RustConstructorError::VariableNotString { variable_name } => (
1819 format!("Variable is not string: {}", variable_name,),
1820 "Please check whether the variable names and types are correct and whether there are duplicate items.",
1821 ),
1822 RustConstructorError::VariableNotUInt { variable_name } => (
1823 format!("Variable is not uint: {}", variable_name,),
1824 "Please check whether the variable names and types are correct and whether there are duplicate items.",
1825 ),
1826 RustConstructorError::VariableNotVec { variable_name } => (
1827 format!("Variable is not vec: {}", variable_name,),
1828 "Please check whether the variable names and types are correct and whether there are duplicate items.",
1829 ),
1830 RustConstructorError::ResourceNotFound {
1831 resource_name,
1832 resource_type,
1833 } => (
1834 format!(
1835 "Resource not found: {}(\"{}\")",
1836 resource_type, resource_name,
1837 ),
1838 "Please check whether the resource has been added.",
1839 ),
1840 };
1841 if self.config.rc_strict_mode {
1843 panic!("{}", problem);
1844 } else {
1845 let sound = self.config.problem_report_sound.clone();
1846 std::thread::spawn(move || {
1847 play_wav(&sound).unwrap_or(0_f64);
1848 });
1849 self.problem_list.push(Problem {
1850 severity_level,
1851 problem,
1852 annotation: annotation.to_string(),
1853 report_state: ReportState {
1854 current_page: self.page.clone(),
1855 current_total_runtime: self.timer.total_time,
1856 current_page_runtime: self.timer.now_time,
1857 },
1858 problem_type: problem_type.clone(),
1859 });
1860 };
1861 }
1862
1863 pub fn check_updated(&mut self, name: &str) -> Result<bool, RustConstructorError> {
1865 if let Ok(id) = self.get_resource_index("PageData", name)
1866 && let RCR::PageData(pd) = self.rust_constructor_resource[id].clone()
1867 {
1868 if !pd.change_page_updated {
1869 self.new_page_update(name);
1870 };
1871 return Ok(pd.change_page_updated);
1872 };
1873 self.problem_report(
1874 RustConstructorError::PageNotFound {
1875 page_name: name.to_string(),
1876 },
1877 SeverityLevel::SevereWarning,
1878 );
1879 Err(RustConstructorError::PageNotFound {
1880 page_name: name.to_string(),
1881 })
1882 }
1883
1884 pub fn check_enter_updated(&mut self, name: &str) -> Result<bool, RustConstructorError> {
1886 if let Ok(id) = self.get_resource_index("PageData", name)
1887 && let RCR::PageData(pd) = &mut self.rust_constructor_resource[id]
1888 {
1889 let return_value = pd.enter_page_updated;
1890 pd.enter_page_updated = true;
1891 return Ok(return_value);
1892 };
1893 self.problem_report(
1894 RustConstructorError::PageNotFound {
1895 page_name: name.to_string(),
1896 },
1897 SeverityLevel::SevereWarning,
1898 );
1899 Err(RustConstructorError::PageNotFound {
1900 page_name: name.to_string(),
1901 })
1902 }
1903
1904 pub fn new_page_update(&mut self, name: &str) {
1906 if let Ok(id) = self.get_resource_index("PageData", name) {
1907 self.timer.start_time = self.timer.total_time;
1908 self.update_timer();
1909 if let RCR::PageData(pd) = &mut self.rust_constructor_resource[id] {
1910 pd.change_page_updated = true;
1911 };
1912 };
1913 }
1914
1915 pub fn update_frame_stats(&mut self, ctx: &egui::Context) {
1917 let current_time = ctx.input(|i| i.time);
1918 if let Some(last) = self.last_frame_time {
1919 let delta = (current_time - last) as f32;
1920 self.frame_times.push(delta);
1921 const MAX_SAMPLES: usize = 120;
1922 if self.frame_times.len() > MAX_SAMPLES {
1923 let remove_count = self.frame_times.len() - MAX_SAMPLES;
1924 self.frame_times.drain(0..remove_count);
1925 }
1926 }
1927 self.last_frame_time = Some(current_time);
1928 }
1929
1930 pub fn current_fps(&self) -> f32 {
1932 if self.frame_times.is_empty() {
1933 0.0
1934 } else {
1935 1.0 / (self.frame_times.iter().sum::<f32>() / self.frame_times.len() as f32)
1936 }
1937 }
1938
1939 pub fn add_split_time(&mut self, mut split_time: SplitTime) {
1941 split_time.time = [self.timer.now_time, self.timer.total_time];
1942 self.rust_constructor_resource
1943 .push(RCR::SplitTime(split_time));
1944 }
1945
1946 pub fn reset_split_time(&mut self, name: &str) {
1948 if let Ok(id) = self.get_resource_index("SplitTime", name)
1949 && let RCR::SplitTime(st) = &mut self.rust_constructor_resource[id]
1950 {
1951 st.time = [self.timer.now_time, self.timer.total_time];
1952 };
1953 self.problem_report(
1954 RustConstructorError::SplitTimeNotFound {
1955 split_time_name: name.to_string(),
1956 },
1957 SeverityLevel::SevereWarning,
1958 );
1959 }
1960
1961 pub fn split_time(&mut self, name: &str) -> Result<[f32; 2], RustConstructorError> {
1963 if let Ok(id) = self.get_resource_index("SplitTime", name)
1964 && let RCR::SplitTime(st) = self.rust_constructor_resource[id].clone()
1965 {
1966 return Ok(st.time);
1967 };
1968 self.problem_report(
1969 RustConstructorError::SplitTimeNotFound {
1970 split_time_name: name.to_string(),
1971 },
1972 SeverityLevel::SevereWarning,
1973 );
1974 Err(RustConstructorError::SplitTimeNotFound {
1975 split_time_name: name.to_string(),
1976 })
1977 }
1978
1979 pub fn update_timer(&mut self) {
1981 let elapsed = self.timer.timer.elapsed();
1982 let seconds = elapsed.as_secs();
1983 let milliseconds = elapsed.subsec_millis();
1984 self.timer.total_time = seconds as f32 + milliseconds as f32 / 1000.0;
1985 self.timer.now_time = self.timer.total_time - self.timer.start_time
1986 }
1987
1988 pub fn add_custom_rect(&mut self, custom_rect: CustomRect) {
1990 self.rust_constructor_resource
1991 .push(RCR::CustomRect(custom_rect));
1992 }
1993
1994 pub fn custom_rect(&mut self, ui: &mut Ui, name: &str, ctx: &egui::Context) {
1996 if let Ok(id) = self.get_resource_index("CustomRect", name)
1997 && let RCR::CustomRect(cr) = &mut self.rust_constructor_resource[id]
1998 {
1999 cr.reg_render_resource(&mut self.render_resource_list);
2000 cr.position[0] = match cr.x_grid[1] {
2001 0 => cr.origin_position[0],
2002 _ => {
2003 (ctx.available_rect().width() as f64 / cr.x_grid[1] as f64
2004 * cr.x_grid[0] as f64) as f32
2005 + cr.origin_position[0]
2006 }
2007 };
2008 cr.position[1] = match cr.y_grid[1] {
2009 0 => cr.origin_position[1],
2010 _ => {
2011 (ctx.available_rect().height() as f64 / cr.y_grid[1] as f64
2012 * cr.y_grid[0] as f64) as f32
2013 + cr.origin_position[1]
2014 }
2015 };
2016 let pos_x = match cr.center_display.0 {
2017 HorizontalAlign::Left => cr.position[0],
2018 HorizontalAlign::Center => cr.position[0] - cr.size[0] / 2.0,
2019 HorizontalAlign::Right => cr.position[0] - cr.size[0],
2020 };
2021 let pos_y = match cr.center_display.1 {
2022 VerticalAlign::Top => cr.position[1],
2023 VerticalAlign::Center => cr.position[1] - cr.size[1] / 2.0,
2024 VerticalAlign::Bottom => cr.position[1] - cr.size[1],
2025 };
2026 ui.painter().rect(
2027 Rect::from_min_max(
2028 Pos2::new(pos_x, pos_y),
2029 Pos2::new(pos_x + cr.size[0], pos_y + cr.size[1]),
2030 ),
2031 cr.rounding,
2032 Color32::from_rgba_unmultiplied(cr.color[0], cr.color[1], cr.color[2], cr.color[3]),
2033 Stroke {
2034 width: cr.border_width,
2035 color: Color32::from_rgba_unmultiplied(
2036 cr.border_color[0],
2037 cr.border_color[1],
2038 cr.border_color[2],
2039 cr.border_color[3],
2040 ),
2041 },
2042 egui::StrokeKind::Inside,
2043 );
2044 };
2045 }
2046
2047 pub fn add_text(&mut self, text: Text) {
2049 self.rust_constructor_resource.push(RCR::Text(text));
2050 }
2051
2052 pub fn text(&mut self, ui: &mut Ui, name: &str, ctx: &egui::Context) {
2054 if let Ok(id) = self.get_resource_index("Text", name)
2055 && let RCR::Text(mut t) = self.rust_constructor_resource[id].clone()
2056 {
2057 t.reg_render_resource(&mut self.render_resource_list);
2058 let galley = ui.fonts(|f| {
2060 f.layout(
2061 t.content.to_string(),
2062 if self.check_resource_exists("Font", &t.font.clone()) {
2063 FontId::new(t.font_size, egui::FontFamily::Name(t.font.clone().into()))
2064 } else {
2065 FontId::proportional(t.font_size)
2066 },
2067 Color32::from_rgba_unmultiplied(t.color[0], t.color[1], t.color[2], t.color[3]),
2068 t.wrap_width,
2069 )
2070 });
2071 let text_size = galley.size();
2072 t.position[0] = match t.x_grid[1] {
2073 0 => t.origin_position[0],
2074 _ => {
2075 (ctx.available_rect().width() as f64 / t.x_grid[1] as f64 * t.x_grid[0] as f64)
2076 as f32
2077 + t.origin_position[0]
2078 }
2079 };
2080 t.position[1] = match t.y_grid[1] {
2081 0 => t.origin_position[1],
2082 _ => {
2083 (ctx.available_rect().height() as f64 / t.y_grid[1] as f64 * t.y_grid[0] as f64)
2084 as f32
2085 + t.origin_position[1]
2086 }
2087 };
2088 let pos_x = match t.center_display.0 {
2089 HorizontalAlign::Left => t.position[0],
2090 HorizontalAlign::Center => t.position[0] - text_size.x / 2.0,
2091 HorizontalAlign::Right => t.position[0] - text_size.x,
2092 };
2093 let pos_y = match t.center_display.1 {
2094 VerticalAlign::Top => t.position[1],
2095 VerticalAlign::Center => t.position[1] - text_size.y / 2.0,
2096 VerticalAlign::Bottom => t.position[1] - text_size.y,
2097 };
2098 let position = Pos2::new(pos_x, pos_y);
2100
2101 if t.write_background {
2102 let rect = Rect::from_min_size(position, text_size);
2103 ui.painter().rect_filled(
2105 rect,
2106 t.background_rounding,
2107 Color32::from_rgba_unmultiplied(
2108 t.background_color[0],
2109 t.background_color[1],
2110 t.background_color[2],
2111 t.background_color[3],
2112 ),
2113 ); };
2115 ui.painter().galley(
2117 position,
2118 galley.clone(),
2119 Color32::from_rgba_unmultiplied(
2120 t.color[0], t.color[1], t.color[2], t.color[3], ),
2122 );
2123
2124 if t.last_frame_content != t.content {
2126 t.hyperlink_index.clear();
2127 for (text, method) in &t.hyperlink_text {
2128 let matches: Vec<(usize, &str)> = t.content.match_indices(text).collect();
2129 if let HyperlinkSelectMethod::All(url) = method {
2130 for (index, _) in matches {
2131 t.hyperlink_index
2132 .push((index, index + text.len(), url.clone()));
2133 }
2134 } else if let HyperlinkSelectMethod::Segment(list) = method {
2135 for (index, url) in list {
2136 if *index >= matches.len() {
2137 continue;
2138 };
2139 t.hyperlink_index.push((
2140 matches[*index].0,
2141 matches[*index].0 + text.len(),
2142 url.clone(),
2143 ));
2144 }
2145 };
2146 }
2147 };
2148
2149 for (start, end, _) in &t.hyperlink_index {
2151 let start_cursor = galley.pos_from_cursor(CCursor::new(*start));
2153 let end_cursor = galley.pos_from_cursor(CCursor::new(*end));
2154
2155 let start_pos = start_cursor.left_top();
2156 let end_pos = end_cursor.right_top();
2157 if start_cursor.min.y == end_cursor.min.y {
2160 let underline_y = position.y
2162 + start_pos.y
2163 + galley.rows.first().map_or(14.0, |row| row.height())
2164 - 2.0;
2165
2166 let color = Color32::from_rgba_unmultiplied(
2168 t.color[0], t.color[1], t.color[2], t.color[3],
2169 );
2170
2171 ui.painter().line_segment(
2172 [
2173 Pos2::new(position.x + start_pos.x, underline_y),
2174 Pos2::new(position.x + end_pos.x, underline_y),
2175 ],
2176 Stroke::new(t.font_size / 10_f32, color),
2177 );
2178 } else {
2179 let row_height = galley.rows.first().map_or(14.0, |row| row.height()); let start_row = (start_pos.y / row_height).round() as usize;
2184 let end_row = (end_pos.y / row_height).round() as usize;
2185
2186 for row in start_row..=end_row {
2187 let row_y = position.y + row as f32 * row_height + row_height - 2.0; if let Some(current_row) = galley.rows.get(row) {
2191 let row_rect = current_row.rect();
2192
2193 let color = Color32::from_rgba_unmultiplied(
2194 t.color[0], t.color[1], t.color[2], t.color[3],
2195 );
2196
2197 if row == start_row {
2198 ui.painter().line_segment(
2200 [
2201 Pos2::new(position.x + start_pos.x, row_y),
2202 Pos2::new(position.x + row_rect.max.x, row_y),
2203 ],
2204 Stroke::new(t.font_size / 10_f32, color),
2205 );
2206 } else if row == end_row {
2207 ui.painter().line_segment(
2209 [
2210 Pos2::new(position.x + row_rect.min.x, row_y),
2211 Pos2::new(position.x + end_pos.x, row_y),
2212 ],
2213 Stroke::new(t.font_size / 10_f32, color),
2214 );
2215 } else {
2216 ui.painter().line_segment(
2218 [
2219 Pos2::new(position.x + row_rect.min.x, row_y),
2220 Pos2::new(position.x + row_rect.max.x, row_y),
2221 ],
2222 Stroke::new(t.font_size / 10_f32, color),
2223 );
2224 };
2225 };
2226 }
2227 };
2228 }
2229
2230 if t.selectable {
2231 let rect = Rect::from_min_size(
2232 [position[0] - 20_f32, position[1] - 5_f32].into(),
2233 [text_size[0] + 40_f32, text_size[1] + 10_f32].into(),
2234 );
2235
2236 let rect2 = Rect::from_min_size(
2237 [0_f32, 0_f32].into(),
2238 [ctx.available_rect().width(), ctx.available_rect().height()].into(),
2239 );
2240
2241 let response = ui.interact(
2243 rect,
2244 egui::Id::new(format!("text_{}_click_and_drag", t.name)),
2245 egui::Sense::click_and_drag(),
2246 );
2247
2248 let response2 = ui.interact(
2249 rect2,
2250 egui::Id::new(format!("text_{}_total", t.name)),
2251 egui::Sense::click(),
2252 );
2253
2254 let cursor_at_pointer = |pointer_pos: Vec2| -> usize {
2256 let relative_pos = pointer_pos - position.to_vec2();
2257 let cursor = galley.cursor_from_pos(relative_pos);
2258 cursor.index
2259 };
2260
2261 if !response.clicked() && response2.clicked() {
2262 t.selection = None;
2263 };
2264
2265 if response.clicked() || response.drag_started() {
2266 if let Some(pointer_pos) = ui.input(|i| i.pointer.interact_pos()) {
2267 let cursor = cursor_at_pointer(pointer_pos.to_vec2());
2268 t.selection = Some((cursor, cursor));
2269 };
2270 response.request_focus();
2271 };
2272
2273 if response.dragged()
2274 && t.selection.is_some()
2275 && let Some(pointer_pos) = ui.input(|i| i.pointer.interact_pos())
2276 {
2277 let cursor = cursor_at_pointer(pointer_pos.to_vec2());
2278 if let Some((start, _)) = t.selection {
2279 t.selection = Some((start, cursor));
2280 };
2281 };
2282
2283 if response.has_focus() {
2285 let copy_triggered = ui.input(|input| {
2287 let c_released = input.key_released(egui::Key::C);
2288 let cmd_pressed = input.modifiers.command || input.modifiers.mac_cmd;
2289 let ctrl_pressed = input.modifiers.ctrl;
2290 c_released && (cmd_pressed || ctrl_pressed)
2291 });
2292 if copy_triggered && let Some((start, end)) = t.selection {
2293 let (start, end) = (start.min(end), start.max(end));
2294 let chars: Vec<char> = t.content.chars().collect();
2295 if start <= chars.len() && end <= chars.len() && start < end {
2296 let selected_text: String = chars[start..end].iter().collect();
2297 ui.ctx().copy_text(selected_text);
2298 };
2299 };
2300 };
2301
2302 if let Some((start, end)) = t.selection {
2304 let (start, end) = (start.min(end), start.max(end));
2305 if start != end {
2306 let start_cursor = galley.pos_from_cursor(CCursor::new(start));
2308 let end_cursor = galley.pos_from_cursor(CCursor::new(end));
2309
2310 let start_pos = start_cursor.left_top();
2311 let end_pos = end_cursor.right_top();
2312 if start_pos.y == end_pos.y {
2314 let rows = &galley.rows;
2317 let row_height = if !rows.is_empty() {
2318 if let Some(row) = rows.first() {
2320 row.height()
2321 } else {
2322 text_size.y / t.content.lines().count() as f32
2323 }
2324 } else {
2325 text_size.y / t.content.lines().count() as f32
2326 };
2327
2328 let selection_rect = Rect::from_min_max(
2329 Pos2::new(position.x + start_pos.x, position.y + start_pos.y),
2330 Pos2::new(
2331 position.x + end_pos.x,
2332 position.y + start_pos.y + row_height,
2333 ),
2334 );
2335 ui.painter().rect_filled(
2336 selection_rect,
2337 0.0,
2338 Color32::from_rgba_unmultiplied(0, 120, 255, 100),
2339 );
2340 } else {
2341 let rows = &galley.rows;
2343 let row_height = if !rows.is_empty() {
2344 rows[0].height()
2345 } else {
2346 text_size.y / t.content.lines().count() as f32
2347 };
2348
2349 let selection_top = position.y + start_pos.y.min(end_pos.y);
2351 let selection_bottom = position.y + start_pos.y.max(end_pos.y);
2352
2353 let start_row_index = (start_pos.y / row_height).floor() as usize;
2355 let end_row_index = (end_pos.y / row_height).floor() as usize;
2356 let (first_row_index, last_row_index) =
2357 if start_row_index <= end_row_index {
2358 (start_row_index, end_row_index)
2359 } else {
2360 (end_row_index, start_row_index)
2361 };
2362
2363 for (i, row) in rows.iter().enumerate() {
2364 let row_y = position.y + row_height * i as f32;
2365 let row_bottom = row_y + row_height;
2366 if row_bottom > selection_top && row_y <= selection_bottom {
2368 let left = if i == first_row_index {
2369 position.x + start_pos.x
2371 } else {
2372 position.x + row.rect().min.x
2374 };
2375
2376 let right = if i == last_row_index {
2377 position.x + end_pos.x
2379 } else {
2380 position.x + row.rect().max.x
2382 };
2383
2384 let selection_rect = Rect::from_min_max(
2385 Pos2::new(left, row_y),
2386 Pos2::new(right, row_bottom),
2387 );
2388
2389 if selection_rect.width() > 0.0 && selection_rect.height() > 0.0
2391 {
2392 ui.painter().rect_filled(
2393 selection_rect,
2394 0.0,
2395 Color32::from_rgba_unmultiplied(0, 120, 255, 100),
2396 );
2397 };
2398 };
2399 }
2400 };
2401 };
2402 };
2403 };
2404
2405 for (start, end, url) in &t.hyperlink_index {
2407 let start_cursor = galley.pos_from_cursor(CCursor::new(*start));
2409 let end_cursor = galley.pos_from_cursor(CCursor::new(*end));
2410
2411 let start_pos = start_cursor.left_top();
2412 let end_pos = end_cursor.right_top();
2413
2414 let row_height = galley.rows.first().map_or(14.0, |row| row.height());
2415
2416 let link_responses = if start_cursor.min.y == end_cursor.min.y {
2418 let link_rect = Rect::from_min_max(
2420 Pos2::new(position.x + start_pos.x, position.y + start_pos.y),
2421 Pos2::new(
2422 position.x + end_pos.x,
2423 position.y + start_pos.y + row_height,
2424 ),
2425 );
2426 vec![ui.interact(
2427 link_rect,
2428 egui::Id::new(format!("link_{}_{}_{}", t.name, start, end)),
2429 egui::Sense::click(),
2430 )]
2431 } else {
2432 let start_row = (start_pos.y / row_height).round() as usize;
2434 let end_row = (end_pos.y / row_height).round() as usize;
2435 let mut responses = Vec::new();
2436
2437 for row in start_row..=end_row {
2438 if let Some(current_row) = galley.rows.get(row) {
2439 let row_rect = current_row.rect();
2440 let row_y = position.y + row as f32 * row_height;
2441
2442 let link_rect = if row == start_row {
2443 Rect::from_min_max(
2445 Pos2::new(position.x + start_pos.x, row_y),
2446 Pos2::new(position.x + row_rect.max.x, row_y + row_height),
2447 )
2448 } else if row == end_row {
2449 Rect::from_min_max(
2451 Pos2::new(position.x + row_rect.min.x, row_y),
2452 Pos2::new(position.x + end_pos.x, row_y + row_height),
2453 )
2454 } else {
2455 Rect::from_min_max(
2457 Pos2::new(position.x + row_rect.min.x, row_y),
2458 Pos2::new(position.x + row_rect.max.x, row_y + row_height),
2459 )
2460 };
2461
2462 responses.push(ui.interact(
2463 link_rect,
2464 egui::Id::new(format!(
2465 "link_{}_{}_{}_row_{}",
2466 t.name, start, end, row
2467 )),
2468 egui::Sense::click(),
2469 ));
2470 };
2471 }
2472 responses
2473 };
2474
2475 let mut is_pressing_link = false;
2477 for link_response in &link_responses {
2478 if link_response.is_pointer_button_down_on() && !link_response.drag_started() {
2479 t.selection = None;
2480 if let Some(pointer_pos) = ui.input(|i| i.pointer.interact_pos()) {
2481 let relative_pos = pointer_pos - position.to_vec2();
2482 let cursor = galley.cursor_from_pos(relative_pos.to_vec2());
2483 if cursor.index >= *start && cursor.index <= *end {
2484 is_pressing_link = true;
2485 break;
2486 };
2487 };
2488 };
2489 }
2490
2491 let mut clicked_on_link = false;
2493 for link_response in &link_responses {
2494 if link_response.clicked()
2495 && let Some(pointer_pos) = ui.input(|i| i.pointer.interact_pos())
2496 {
2497 let relative_pos = pointer_pos - position.to_vec2();
2498 let cursor = galley.cursor_from_pos(relative_pos.to_vec2());
2499 if cursor.index >= *start && cursor.index <= *end {
2500 clicked_on_link = true;
2501 break;
2502 };
2503 };
2504 }
2505
2506 if clicked_on_link {
2507 if !url.is_empty() {
2509 ui.ctx().open_url(egui::OpenUrl::new_tab(url));
2510 };
2511 };
2512
2513 if is_pressing_link {
2515 if start_cursor.min.y == end_cursor.min.y {
2516 let selection_rect = Rect::from_min_max(
2518 Pos2::new(position.x + start_pos.x, position.y + start_pos.y),
2519 Pos2::new(
2520 position.x + end_pos.x,
2521 position.y
2522 + start_pos.y
2523 + galley.rows.first().map_or(14.0, |row| row.height()),
2524 ),
2525 );
2526 ui.painter().rect_filled(
2527 selection_rect,
2528 0.0,
2529 Color32::from_rgba_unmultiplied(0, 120, 255, 100),
2530 );
2531 } else {
2532 let row_height = galley.rows.first().map_or(14.0, |row| row.height());
2534 let start_row = (start_pos.y / row_height).round() as usize;
2535 let end_row = (end_pos.y / row_height).round() as usize;
2536
2537 for row in start_row..=end_row {
2538 if let Some(current_row) = galley.rows.get(row) {
2539 let row_rect = current_row.rect();
2540
2541 if row == start_row {
2542 let selection_rect = Rect::from_min_max(
2544 Pos2::new(
2545 position.x + start_pos.x,
2546 position.y + row as f32 * row_height,
2547 ),
2548 Pos2::new(
2549 position.x + row_rect.max.x,
2550 position.y + row as f32 * row_height + row_height,
2551 ),
2552 );
2553 ui.painter().rect_filled(
2554 selection_rect,
2555 0.0,
2556 Color32::from_rgba_unmultiplied(0, 120, 255, 100),
2557 );
2558 } else if row == end_row {
2559 let selection_rect = Rect::from_min_max(
2561 Pos2::new(
2562 position.x + row_rect.min.x,
2563 position.y + row as f32 * row_height,
2564 ),
2565 Pos2::new(
2566 position.x + end_pos.x,
2567 position.y + row as f32 * row_height + row_height,
2568 ),
2569 );
2570 ui.painter().rect_filled(
2571 selection_rect,
2572 0.0,
2573 Color32::from_rgba_unmultiplied(0, 120, 255, 100),
2574 );
2575 } else {
2576 let selection_rect = Rect::from_min_max(
2578 Pos2::new(
2579 position.x + row_rect.min.x,
2580 position.y + row as f32 * row_height,
2581 ),
2582 Pos2::new(
2583 position.x + row_rect.max.x,
2584 position.y + row as f32 * row_height + row_height,
2585 ),
2586 );
2587 ui.painter().rect_filled(
2588 selection_rect,
2589 0.0,
2590 Color32::from_rgba_unmultiplied(0, 120, 255, 100),
2591 );
2592 };
2593 };
2594 }
2595 };
2596 };
2597 }
2598 t.last_frame_content = t.content.clone();
2599 self.rust_constructor_resource[id] = RCR::Text(t);
2600 };
2601 }
2602
2603 pub fn get_text_size(
2605 &mut self,
2606 resource_name: &str,
2607 ui: &mut Ui,
2608 ) -> Result<[f32; 2], RustConstructorError> {
2609 if let Ok(id) = self.get_resource_index("Text", resource_name)
2610 && let RCR::Text(t) = self.rust_constructor_resource[id].clone()
2611 {
2612 let galley = ui.fonts(|f| {
2613 f.layout(
2614 t.content.to_string(),
2615 FontId::proportional(t.font_size),
2616 Color32::from_rgba_unmultiplied(t.color[0], t.color[1], t.color[2], t.color[3]),
2617 t.wrap_width,
2618 )
2619 });
2620 return Ok([galley.size().x, galley.size().y]);
2621 };
2622 self.problem_report(
2623 RustConstructorError::TextNotFound {
2624 text_name: resource_name.to_string(),
2625 },
2626 SeverityLevel::SevereWarning,
2627 );
2628 Err(RustConstructorError::TextNotFound {
2629 text_name: resource_name.to_string(),
2630 })
2631 }
2632
2633 pub fn add_var(&mut self, variable: Variable) {
2635 self.rust_constructor_resource.push(RCR::Variable(variable));
2636 }
2637
2638 pub fn modify_var<T: Into<Value>>(&mut self, name: &str, value: T) {
2640 if let Ok(id) = self.get_resource_index("Variable", name)
2641 && let RCR::Variable(v) = &mut self.rust_constructor_resource[id]
2642 {
2643 v.value = value.into();
2644 };
2645 }
2646
2647 pub fn var(&mut self, name: &str) -> Result<Value, RustConstructorError> {
2649 if let Ok(id) = self.get_resource_index("Variable", name)
2650 && let RCR::Variable(v) = self.rust_constructor_resource[id].clone()
2651 {
2652 return Ok(v.clone().value);
2653 };
2654 self.problem_report(
2655 RustConstructorError::VariableNotFound {
2656 variable_name: name.to_string(),
2657 },
2658 SeverityLevel::SevereWarning,
2659 );
2660 Err(RustConstructorError::VariableNotFound {
2661 variable_name: name.to_string(),
2662 })
2663 }
2664
2665 pub fn var_i(&mut self, name: &str) -> Result<i32, RustConstructorError> {
2667 if let Ok(id) = self.get_resource_index("Variable", name) {
2668 if let RCR::Variable(v) = self.rust_constructor_resource[id].clone() {
2669 match &v.value {
2670 Value::Int(i) => Ok(*i),
2672 _ => {
2673 self.problem_report(
2674 RustConstructorError::VariableNotInt {
2675 variable_name: name.to_string(),
2676 },
2677 SeverityLevel::SevereWarning,
2678 );
2679 Err(RustConstructorError::VariableNotInt {
2680 variable_name: name.to_string(),
2681 })
2682 }
2683 }
2684 } else {
2685 Err(RustConstructorError::VariableNotFound {
2687 variable_name: name.to_string(),
2688 })
2689 }
2690 } else {
2691 self.problem_report(
2692 RustConstructorError::VariableNotFound {
2693 variable_name: name.to_string(),
2694 },
2695 SeverityLevel::SevereWarning,
2696 );
2697 Err(RustConstructorError::VariableNotFound {
2698 variable_name: name.to_string(),
2699 })
2700 }
2701 }
2702
2703 pub fn var_u(&mut self, name: &str) -> Result<u32, RustConstructorError> {
2705 if let Ok(id) = self.get_resource_index("Variable", name) {
2706 if let RCR::Variable(v) = self.rust_constructor_resource[id].clone() {
2707 match &v.value {
2708 Value::UInt(u) => Ok(*u),
2710 _ => {
2711 self.problem_report(
2712 RustConstructorError::VariableNotUInt {
2713 variable_name: name.to_string(),
2714 },
2715 SeverityLevel::SevereWarning,
2716 );
2717 Err(RustConstructorError::VariableNotUInt {
2718 variable_name: name.to_string(),
2719 })
2720 }
2721 }
2722 } else {
2723 Err(RustConstructorError::VariableNotFound {
2725 variable_name: name.to_string(),
2726 })
2727 }
2728 } else {
2729 self.problem_report(
2730 RustConstructorError::VariableNotFound {
2731 variable_name: name.to_string(),
2732 },
2733 SeverityLevel::SevereWarning,
2734 );
2735 Err(RustConstructorError::VariableNotFound {
2736 variable_name: name.to_string(),
2737 })
2738 }
2739 }
2740
2741 pub fn var_f(&mut self, name: &str) -> Result<f32, RustConstructorError> {
2743 if let Ok(id) = self.get_resource_index("Variable", name) {
2744 if let RCR::Variable(v) = self.rust_constructor_resource[id].clone() {
2745 match &v.value {
2746 Value::Float(f) => Ok(*f),
2748 _ => {
2749 self.problem_report(
2750 RustConstructorError::VariableNotFloat {
2751 variable_name: name.to_string(),
2752 },
2753 SeverityLevel::SevereWarning,
2754 );
2755 Err(RustConstructorError::VariableNotFloat {
2756 variable_name: name.to_string(),
2757 })
2758 }
2759 }
2760 } else {
2761 Err(RustConstructorError::VariableNotFound {
2763 variable_name: name.to_string(),
2764 })
2765 }
2766 } else {
2767 self.problem_report(
2768 RustConstructorError::VariableNotFound {
2769 variable_name: name.to_string(),
2770 },
2771 SeverityLevel::SevereWarning,
2772 );
2773 Err(RustConstructorError::VariableNotFound {
2774 variable_name: name.to_string(),
2775 })
2776 }
2777 }
2778
2779 pub fn var_b(&mut self, name: &str) -> Result<bool, RustConstructorError> {
2781 if let Ok(id) = self.get_resource_index("Variable", name) {
2782 if let RCR::Variable(v) = self.rust_constructor_resource[id].clone() {
2783 match &v.value {
2784 Value::Bool(b) => Ok(*b),
2786 _ => {
2787 self.problem_report(
2788 RustConstructorError::VariableNotBool {
2789 variable_name: name.to_string(),
2790 },
2791 SeverityLevel::SevereWarning,
2792 );
2793 Err(RustConstructorError::VariableNotBool {
2794 variable_name: name.to_string(),
2795 })
2796 }
2797 }
2798 } else {
2799 Err(RustConstructorError::VariableNotFound {
2801 variable_name: name.to_string(),
2802 })
2803 }
2804 } else {
2805 self.problem_report(
2806 RustConstructorError::VariableNotFound {
2807 variable_name: name.to_string(),
2808 },
2809 SeverityLevel::SevereWarning,
2810 );
2811 Err(RustConstructorError::VariableNotFound {
2812 variable_name: name.to_string(),
2813 })
2814 }
2815 }
2816
2817 pub fn var_v(&mut self, name: &str) -> Result<Vec<Value>, RustConstructorError> {
2819 if let Ok(id) = self.get_resource_index("Variable", name) {
2820 if let RCR::Variable(v) = self.rust_constructor_resource[id].clone() {
2821 match &v.value {
2822 Value::Vec(v) => Ok(v.clone()),
2824 _ => {
2825 self.problem_report(
2826 RustConstructorError::VariableNotVec {
2827 variable_name: name.to_string(),
2828 },
2829 SeverityLevel::SevereWarning,
2830 );
2831 Err(RustConstructorError::VariableNotVec {
2832 variable_name: name.to_string(),
2833 })
2834 }
2835 }
2836 } else {
2837 Err(RustConstructorError::VariableNotFound {
2839 variable_name: name.to_string(),
2840 })
2841 }
2842 } else {
2843 self.problem_report(
2844 RustConstructorError::VariableNotFound {
2845 variable_name: name.to_string(),
2846 },
2847 SeverityLevel::SevereWarning,
2848 );
2849 Err(RustConstructorError::VariableNotFound {
2850 variable_name: name.to_string(),
2851 })
2852 }
2853 }
2854
2855 pub fn var_s(&mut self, name: &str) -> Result<String, RustConstructorError> {
2857 if let Ok(id) = self.get_resource_index("Variable", name) {
2858 if let RCR::Variable(v) = self.rust_constructor_resource[id].clone() {
2859 match &v.value {
2860 Value::String(s) => Ok(s.clone()),
2862 _ => {
2863 self.problem_report(
2864 RustConstructorError::VariableNotString {
2865 variable_name: name.to_string(),
2866 },
2867 SeverityLevel::SevereWarning,
2868 );
2869 Err(RustConstructorError::VariableNotString {
2870 variable_name: name.to_string(),
2871 })
2872 }
2873 }
2874 } else {
2875 Err(RustConstructorError::VariableNotFound {
2877 variable_name: name.to_string(),
2878 })
2879 }
2880 } else {
2881 self.problem_report(
2882 RustConstructorError::VariableNotFound {
2883 variable_name: name.to_string(),
2884 },
2885 SeverityLevel::SevereWarning,
2886 );
2887 Err(RustConstructorError::VariableNotFound {
2888 variable_name: name.to_string(),
2889 })
2890 }
2891 }
2892
2893 pub fn var_decode_b(&mut self, target: Value) -> Result<bool, RustConstructorError> {
2895 match target {
2896 Value::Bool(b) => {
2897 Ok(b)
2899 }
2900 _ => {
2901 self.problem_report(
2902 RustConstructorError::VariableNotBool {
2903 variable_name: format!("{:?}", target),
2904 },
2905 SeverityLevel::SevereWarning,
2906 );
2907 Err(RustConstructorError::VariableNotBool {
2908 variable_name: format!("{:?}", target),
2909 })
2910 }
2911 }
2912 }
2913
2914 pub fn var_decode_i(&mut self, target: Value) -> Result<i32, RustConstructorError> {
2916 match target {
2917 Value::Int(i) => {
2918 Ok(i)
2920 }
2921 _ => {
2922 self.problem_report(
2923 RustConstructorError::VariableNotInt {
2924 variable_name: format!("{:?}", target),
2925 },
2926 SeverityLevel::SevereWarning,
2927 );
2928 Err(RustConstructorError::VariableNotInt {
2929 variable_name: format!("{:?}", target),
2930 })
2931 }
2932 }
2933 }
2934
2935 pub fn var_decode_u(&mut self, target: Value) -> Result<u32, RustConstructorError> {
2937 match target {
2938 Value::UInt(u) => {
2939 Ok(u)
2941 }
2942 _ => {
2943 self.problem_report(
2944 RustConstructorError::VariableNotUInt {
2945 variable_name: format!("{:?}", target),
2946 },
2947 SeverityLevel::SevereWarning,
2948 );
2949 Err(RustConstructorError::VariableNotUInt {
2950 variable_name: format!("{:?}", target),
2951 })
2952 }
2953 }
2954 }
2955
2956 pub fn var_decode_f(&mut self, target: Value) -> Result<f32, RustConstructorError> {
2958 match target {
2959 Value::Float(f) => {
2960 Ok(f)
2962 }
2963 _ => {
2964 self.problem_report(
2965 RustConstructorError::VariableNotFloat {
2966 variable_name: format!("{:?}", target),
2967 },
2968 SeverityLevel::SevereWarning,
2969 );
2970 Err(RustConstructorError::VariableNotFloat {
2971 variable_name: format!("{:?}", target),
2972 })
2973 }
2974 }
2975 }
2976
2977 pub fn var_decode_s(&mut self, target: Value) -> Result<String, RustConstructorError> {
2979 match target {
2980 Value::String(s) => {
2981 Ok(s)
2983 }
2984 _ => {
2985 self.problem_report(
2986 RustConstructorError::VariableNotString {
2987 variable_name: format!("{:?}", target),
2988 },
2989 SeverityLevel::SevereWarning,
2990 );
2991 Err(RustConstructorError::VariableNotString {
2992 variable_name: format!("{:?}", target),
2993 })
2994 }
2995 }
2996 }
2997
2998 pub fn var_decode_v(&mut self, target: Value) -> Result<Vec<Value>, RustConstructorError> {
3000 match target {
3001 Value::Vec(v) => {
3002 Ok(v)
3004 }
3005 _ => {
3006 self.problem_report(
3007 RustConstructorError::VariableNotVec {
3008 variable_name: format!("{:?}", target),
3009 },
3010 SeverityLevel::SevereWarning,
3011 );
3012 Err(RustConstructorError::VariableNotVec {
3013 variable_name: format!("{:?}", target),
3014 })
3015 }
3016 }
3017 }
3018
3019 pub fn add_image_texture(
3021 &mut self,
3022 mut image_texture: ImageTexture,
3023 path: &str,
3024 flip: [bool; 2],
3025 ctx: &egui::Context,
3026 ) {
3027 if let Ok(mut file) = File::open(path) {
3028 let mut buffer = Vec::new();
3029 file.read_to_end(&mut buffer).unwrap();
3030 let img_bytes = buffer;
3031 let img = image::load_from_memory(&img_bytes).unwrap();
3032 let color_data = match flip {
3033 [true, true] => img.fliph().flipv().into_rgba8(),
3034 [true, false] => img.fliph().into_rgba8(),
3035 [false, true] => img.flipv().into_rgba8(),
3036 _ => img.into_rgba8(),
3037 };
3038 let (w, h) = (color_data.width(), color_data.height());
3039 let raw_data: Vec<u8> = color_data.into_raw();
3040
3041 let color_image =
3042 egui::ColorImage::from_rgba_unmultiplied([w as usize, h as usize], &raw_data);
3043 let loaded_image_texture = Some(ctx.load_texture(
3044 image_texture.name.clone(),
3045 color_image,
3046 TextureOptions::LINEAR,
3047 ));
3048 image_texture.texture = loaded_image_texture;
3049 image_texture.cite_path = path.to_string();
3050 self.rust_constructor_resource
3051 .push(RCR::ImageTexture(image_texture));
3052 } else {
3053 self.problem_report(
3054 RustConstructorError::ImageGetFailed {
3055 image_path: path.to_string(),
3056 },
3057 SeverityLevel::SevereWarning,
3058 );
3059 };
3060 }
3061
3062 pub fn image_texture(
3064 &mut self,
3065 name: &str,
3066 ) -> Result<Option<egui::TextureHandle>, RustConstructorError> {
3067 if let Ok(id) = self.get_resource_index("ImageTexture", name)
3068 && let RCR::ImageTexture(it) = &self.rust_constructor_resource[id]
3069 {
3070 return Ok(it.texture.clone());
3071 };
3072 self.problem_report(
3073 RustConstructorError::ImageNotFound {
3074 image_name: name.to_string(),
3075 },
3076 SeverityLevel::SevereWarning,
3077 );
3078 Err(RustConstructorError::ImageNotFound {
3079 image_name: name.to_string(),
3080 })
3081 }
3082
3083 pub fn reset_image_texture(
3085 &mut self,
3086 name: &str,
3087 path: &str,
3088 flip: [bool; 2],
3089 ctx: &egui::Context,
3090 ) {
3091 if let Ok(id) = self.get_resource_index("ImageTexture", name)
3092 && let RCR::ImageTexture(it) = &mut self.rust_constructor_resource[id]
3093 {
3094 if let Ok(mut file) = File::open(path) {
3095 let mut buffer = Vec::new();
3096 file.read_to_end(&mut buffer).unwrap();
3097 let img_bytes = buffer;
3098 let img = image::load_from_memory(&img_bytes).unwrap();
3099 let color_data = match flip {
3100 [true, true] => img.fliph().flipv().into_rgba8(),
3101 [true, false] => img.fliph().into_rgba8(),
3102 [false, true] => img.flipv().into_rgba8(),
3103 _ => img.into_rgba8(),
3104 };
3105 let (w, h) = (color_data.width(), color_data.height());
3106 let raw_data: Vec<u8> = color_data.into_raw();
3107
3108 let color_image =
3109 egui::ColorImage::from_rgba_unmultiplied([w as usize, h as usize], &raw_data);
3110 let image_texture =
3111 Some(ctx.load_texture(it.name.clone(), color_image, TextureOptions::LINEAR));
3112 it.texture = image_texture;
3113 it.cite_path = path.to_string();
3114 } else {
3115 self.problem_report(
3116 RustConstructorError::ImageGetFailed {
3117 image_path: path.to_string(),
3118 },
3119 SeverityLevel::SevereWarning,
3120 );
3121 };
3122 };
3123 }
3124
3125 pub fn add_image(&mut self, mut image: Image, image_texture_name: &str) {
3127 if let Ok(id) = self.get_resource_index("ImageTexture", image_texture_name)
3128 && let RCR::ImageTexture(it) = self.rust_constructor_resource[id].clone()
3129 {
3130 image.texture = it.texture;
3131 image.cite_texture = it.name.clone();
3132 image.last_frame_cite_texture = it.name;
3133 self.rust_constructor_resource.push(RCR::Image(image));
3134 };
3135 }
3136
3137 pub fn image(&mut self, ui: &mut Ui, name: &str, ctx: &egui::Context) {
3139 if let Ok(id) = self.get_resource_index("Image", name)
3140 && let RCR::Image(mut im) = self.rust_constructor_resource[id].clone()
3141 {
3142 if im.cite_texture != im.last_frame_cite_texture
3143 && let Ok(id2) = self.get_resource_index("ImageTexture", &im.cite_texture)
3144 && let RCR::ImageTexture(it) = self.rust_constructor_resource[id2].clone()
3145 {
3146 im.texture = it.texture;
3147 };
3148 im.reg_render_resource(&mut self.render_resource_list);
3149 im.position[0] = match im.x_grid[1] {
3150 0 => im.origin_position[0],
3151 _ => {
3152 (ctx.available_rect().width() as f64 / im.x_grid[1] as f64
3153 * im.x_grid[0] as f64) as f32
3154 + im.origin_position[0]
3155 }
3156 };
3157 im.position[1] = match im.y_grid[1] {
3158 0 => im.origin_position[1],
3159 _ => {
3160 (ctx.available_rect().height() as f64 / im.y_grid[1] as f64
3161 * im.y_grid[0] as f64) as f32
3162 + im.origin_position[1]
3163 }
3164 };
3165 match im.center_display.0 {
3166 HorizontalAlign::Left => {}
3167 HorizontalAlign::Center => im.position[0] -= im.size[0] / 2.0,
3168 HorizontalAlign::Right => im.position[0] -= im.size[0],
3169 };
3170 match im.center_display.1 {
3171 VerticalAlign::Top => {}
3172 VerticalAlign::Center => im.position[1] -= im.size[1] / 2.0,
3173 VerticalAlign::Bottom => im.position[1] -= im.size[1],
3174 };
3175 if let Some(texture) = &im.texture {
3176 let rect = Rect::from_min_size(
3177 Pos2::new(im.position[0], im.position[1]),
3178 Vec2::new(im.size[0], im.size[1]),
3179 );
3180 let color = if im.use_overlay_color {
3181 Color32::from_rgba_unmultiplied(
3183 im.overlay_color[0],
3184 im.overlay_color[1],
3185 im.overlay_color[2],
3186 (im.alpha as f32 * im.overlay_color[3] as f32 / 255.0) as u8,
3188 )
3189 } else {
3190 Color32::from_white_alpha(im.alpha)
3191 };
3192
3193 egui::Image::new(egui::ImageSource::Texture(texture.into()))
3195 .tint(color)
3196 .paint_at(ui, rect)
3197 };
3198 im.last_frame_cite_texture = im.cite_texture.clone();
3199 self.rust_constructor_resource[id] = RCR::Image(im);
3200 };
3201 }
3202
3203 pub fn add_message_box(
3205 &mut self,
3206 mut message_box: MessageBox,
3207 title_name: &str,
3208 content_name: &str,
3209 image_name: &str,
3210 sound_path: &str,
3211 ) {
3212 if !self.check_resource_exists("MessageBox", &message_box.name) {
3213 message_box.exist = true;
3214 message_box.memory_offset = 0_f32;
3215 message_box.image_name = String::new();
3216 if let Ok(id) = self.get_resource_index("Image", image_name)
3217 && let RCR::Image(im) = &mut self.rust_constructor_resource[id]
3218 {
3219 message_box.image_name = image_name.to_string();
3220 im.size = [message_box.size[1] - 15_f32, message_box.size[1] - 15_f32];
3221 im.center_display = (HorizontalAlign::Left, VerticalAlign::Center);
3222 im.x_grid = [1, 1];
3223 im.y_grid = [0, 1];
3224 im.name = format!("MessageBox{}", im.name);
3225 };
3226 message_box.title_name = String::new();
3227 if let Ok(id) = self.get_resource_index("Text", title_name)
3228 && let RCR::Text(t) = &mut self.rust_constructor_resource[id]
3229 {
3230 message_box.title_name = title_name.to_string();
3231 t.x_grid = [1, 1];
3232 t.y_grid = [0, 1];
3233 t.center_display = (HorizontalAlign::Left, VerticalAlign::Top);
3234 t.wrap_width = message_box.size[0] - message_box.size[1] + 5_f32;
3235 t.name = format!("MessageBox{}", t.name);
3236 };
3237 message_box.content_name = String::new();
3238 if let Ok(id) = self.get_resource_index("Text", content_name)
3239 && let RCR::Text(t) = &mut self.rust_constructor_resource[id]
3240 {
3241 message_box.content_name = content_name.to_string();
3242 t.center_display = (HorizontalAlign::Left, VerticalAlign::Top);
3243 t.x_grid = [1, 1];
3244 t.y_grid = [0, 1];
3245 t.wrap_width = message_box.size[0] - message_box.size[1] + 5_f32;
3246 t.name = format!("MessageBox{}", t.name);
3247 };
3248 self.rust_constructor_resource
3249 .push(RCR::MessageBox(message_box.clone()));
3250 if !message_box.keep_existing {
3251 self.add_split_time(
3252 SplitTime::default().name(&format!("MessageBox{}", message_box.name)),
3253 );
3254 };
3255 self.add_split_time(
3256 SplitTime::default().name(&format!("MessageBox{}Animation", message_box.name)),
3257 );
3258 self.add_custom_rect(
3259 CustomRect::default()
3260 .name(&format!("MessageBox{}", message_box.name))
3261 .origin_position(0_f32, 0_f32)
3262 .size(message_box.size[0], message_box.size[1])
3263 .rounding(20_f32)
3264 .x_grid(1, 1)
3265 .y_grid(0, 1)
3266 .center_display(HorizontalAlign::Left, VerticalAlign::Top)
3267 .color(100, 100, 100, 125)
3268 .border_width(0_f32),
3269 );
3270 self.add_image(
3271 Image::default()
3272 .name(&format!("MessageBox{}Close", message_box.name))
3273 .use_overlay_color(false)
3274 .size(30_f32, 30_f32)
3275 .center_display(HorizontalAlign::Center, VerticalAlign::Center),
3276 "CloseMessageBox",
3277 );
3278 self.add_switch(
3279 Switch::default()
3280 .name(&format!("MessageBox{}Close", message_box.name))
3281 .appearance(vec![
3282 SwitchData {
3283 texture: "CloseMessageBox".to_string(),
3284 color: [255, 255, 255, 0],
3285 text: String::new(),
3286 hint_text: String::new(),
3287 },
3288 SwitchData {
3289 texture: "CloseMessageBox".to_string(),
3290 color: [180, 180, 180, 200],
3291 text: String::new(),
3292 hint_text: String::new(),
3293 },
3294 SwitchData {
3295 texture: "CloseMessageBox".to_string(),
3296 color: [255, 255, 255, 200],
3297 text: String::new(),
3298 hint_text: String::new(),
3299 },
3300 SwitchData {
3301 texture: "CloseMessageBox".to_string(),
3302 color: [180, 180, 180, 200],
3303 text: String::new(),
3304 hint_text: String::new(),
3305 },
3306 ])
3307 .enable_hover_click_image(false, true)
3308 .click_method(vec![SwitchClickAction {
3309 click_method: PointerButton::Primary,
3310 action: true,
3311 }])
3312 .sound_path(sound_path),
3313 &format!("MessageBox{}Close", message_box.name),
3314 "",
3315 );
3316 } else {
3317 self.problem_report(
3318 RustConstructorError::MessageBoxAlreadyExists {
3319 message_box_name: message_box.name.to_string(),
3320 },
3321 SeverityLevel::SevereWarning,
3322 );
3323 };
3324 }
3325
3326 pub fn message_box_display(&mut self, ctx: &egui::Context, ui: &mut Ui) {
3328 let mut offset = 0_f32;
3329 let mut delete_count = 0;
3330 let mut index_list = Vec::new();
3331 for i in 0..self.rust_constructor_resource.len() {
3332 if let RCR::MessageBox(_) = self.rust_constructor_resource[i] {
3333 index_list.push(i);
3334 };
3335 }
3336 for u in 0..index_list.len() {
3337 let mut deleted = false;
3338 let i = u - delete_count;
3339 if let RCR::MessageBox(mut mb) = self.rust_constructor_resource[index_list[i]].clone()
3340 && let Ok(id1) = self.get_resource_index("Image", &mb.image_name)
3341 && let RCR::Image(mut im1) = self.rust_constructor_resource[id1].clone()
3342 && let Ok(id2) =
3343 self.get_resource_index("CustomRect", &format!("MessageBox{}", mb.name))
3344 && let RCR::CustomRect(mut cr) = self.rust_constructor_resource[id2].clone()
3345 && let Ok(id3) = self.get_resource_index("Text", &mb.title_name)
3346 && let RCR::Text(mut t1) = self.rust_constructor_resource[id3].clone()
3347 && let Ok(id4) = self.get_resource_index("Text", &mb.content_name)
3348 && let RCR::Text(mut t2) = self.rust_constructor_resource[id4].clone()
3349 && let Ok(id5) =
3350 self.get_resource_index("Switch", &format!("MessageBox{}Close", mb.name))
3351 && let RCR::Switch(mut s) = self.rust_constructor_resource[id5].clone()
3352 && let Ok(id6) =
3353 self.get_resource_index("Image", &format!("MessageBox{}Close", mb.name))
3354 && let RCR::Image(mut im2) = self.rust_constructor_resource[id6].clone()
3355 {
3356 if mb.size[1]
3357 < self.get_text_size(&mb.title_name.clone(), ui).unwrap()[1]
3358 + self.get_text_size(&mb.content_name.clone(), ui).unwrap()[1]
3359 + 10_f32
3360 {
3361 mb.size[1] = self.get_text_size(&mb.title_name.clone(), ui).unwrap()[1]
3362 + self.get_text_size(&mb.content_name.clone(), ui).unwrap()[1]
3363 + 10_f32;
3364 cr.size[1] = mb.size[1];
3365 im1.size = [mb.size[1] - 15_f32, mb.size[1] - 15_f32];
3366 t1.wrap_width = mb.size[0] - mb.size[1] + 5_f32;
3367 t2.wrap_width = mb.size[0] - mb.size[1] + 5_f32;
3368 };
3369 if self.timer.total_time
3370 - self
3371 .split_time(&format!("MessageBox{}Animation", mb.name))
3372 .unwrap()[1]
3373 >= self.vertrefresh
3374 {
3375 self.reset_split_time(&format!("MessageBox{}Animation", mb.name));
3376 if offset != mb.memory_offset {
3377 if mb.memory_offset < offset {
3378 if mb.memory_offset + mb.restore_speed >= offset {
3379 mb.memory_offset = offset;
3380 } else {
3381 mb.memory_offset += mb.restore_speed;
3382 };
3383 } else if mb.memory_offset - mb.restore_speed <= offset {
3384 mb.memory_offset = offset;
3385 } else {
3386 mb.memory_offset -= mb.restore_speed;
3387 };
3388 };
3389 if cr.origin_position[0] != -mb.size[0] - 5_f32 {
3390 if mb.exist {
3391 if cr.origin_position[0] - mb.speed <= -mb.size[0] - 5_f32 {
3392 cr.origin_position[0] = -mb.size[0] - 5_f32;
3393 if self.check_resource_exists(
3394 "SplitTime",
3395 &format!("MessageBox{}", mb.name),
3396 ) {
3397 self.reset_split_time(&format!("MessageBox{}", mb.name));
3398 };
3399 } else {
3400 cr.origin_position[0] -= mb.speed;
3401 };
3402 } else if cr.origin_position[0] + mb.speed >= 15_f32 {
3403 cr.origin_position[0] = 15_f32;
3404 delete_count += 1;
3405 deleted = true;
3406 } else {
3407 cr.origin_position[0] += mb.speed;
3408 };
3409 };
3410 };
3411 cr.origin_position[1] = mb.memory_offset + 20_f32;
3412 im1.origin_position = [
3413 cr.origin_position[0] + 5_f32,
3414 cr.origin_position[1] + mb.size[1] / 2_f32,
3415 ];
3416 t1.origin_position = [
3417 im1.origin_position[0] + im1.size[0] + 5_f32,
3418 cr.origin_position[1] + 5_f32,
3419 ];
3420 t2.origin_position = [
3421 im1.origin_position[0] + im1.size[0] + 5_f32,
3422 t1.origin_position[1]
3423 + self.get_text_size(&mb.title_name.clone(), ui).unwrap()[1],
3424 ];
3425 im2.origin_position = cr.position;
3426 if !mb.keep_existing
3427 && self.timer.total_time
3428 - self.split_time(&format!("MessageBox{}", mb.name)).unwrap()[1]
3429 >= mb.existing_time
3430 && cr.origin_position[0] == -mb.size[0] - 5_f32
3431 {
3432 mb.exist = false;
3433 if cr.origin_position[0] + mb.speed >= 15_f32 {
3434 cr.origin_position[0] = 15_f32;
3435 } else {
3436 cr.origin_position[0] += mb.speed;
3437 };
3438 };
3439 if let Some(mouse_pos) = ui.input(|i| i.pointer.hover_pos()) {
3440 let rect = egui::Rect::from_min_size(
3441 Pos2 {
3442 x: im2.position[0],
3443 y: im2.position[1],
3444 },
3445 Vec2 {
3446 x: cr.size[0] + 25_f32,
3447 y: cr.size[1] + 25_f32,
3448 },
3449 );
3450 if rect.contains(mouse_pos) {
3451 s.appearance[0].color[3] = 200;
3452 } else {
3453 s.appearance[0].color[3] = 0;
3454 };
3455 };
3456 self.rust_constructor_resource[index_list[i]] = RCR::MessageBox(mb.clone());
3457 self.rust_constructor_resource[id1] = RCR::Image(im1.clone());
3458 self.rust_constructor_resource[id2] = RCR::CustomRect(cr.clone());
3459 self.rust_constructor_resource[id3] = RCR::Text(t1.clone());
3460 self.rust_constructor_resource[id4] = RCR::Text(t2.clone());
3461 self.rust_constructor_resource[id5] = RCR::Switch(s.clone());
3462 self.rust_constructor_resource[id6] = RCR::Image(im2.clone());
3463 self.custom_rect(ui, &format!("MessageBox{}", mb.name), ctx);
3464 self.image(ui, &mb.image_name.clone(), ctx);
3465 self.text(ui, &t1.name.clone(), ctx);
3466 self.text(ui, &t2.name.clone(), ctx);
3467 if self
3468 .switch(
3469 &format!("MessageBox{}Close", mb.name),
3470 ui,
3471 ctx,
3472 s.state == 0 && mb.exist,
3473 true,
3474 )
3475 .unwrap()[0]
3476 == 0
3477 {
3478 mb.exist = false;
3479 if cr.origin_position[0] + mb.speed >= 15_f32 {
3480 cr.origin_position[0] = 15_f32;
3481 } else {
3482 cr.origin_position[0] += mb.speed;
3483 };
3484 self.rust_constructor_resource[id2] = RCR::CustomRect(cr.clone());
3485 self.rust_constructor_resource[index_list[i]] = RCR::MessageBox(mb.clone());
3486 };
3487 if deleted {
3488 if let Ok(id) = self.get_resource_index("Image", &mb.image_name) {
3489 self.rust_constructor_resource.remove(id);
3490 };
3491 if let Ok(id) =
3492 self.get_resource_index("CustomRect", &format!("MessageBox{}", mb.name))
3493 {
3494 self.rust_constructor_resource.remove(id);
3495 };
3496 if let Ok(id) = self.get_resource_index("Text", &mb.title_name) {
3497 self.rust_constructor_resource.remove(id);
3498 };
3499 if let Ok(id) = self.get_resource_index("Text", &mb.content_name) {
3500 self.rust_constructor_resource.remove(id);
3501 };
3502 if let Ok(id) =
3503 self.get_resource_index("Switch", &format!("MessageBox{}Close", mb.name))
3504 {
3505 self.rust_constructor_resource.remove(id);
3506 };
3507 if let Ok(id) =
3508 self.get_resource_index("Image", &format!("MessageBox{}Close", mb.name))
3509 {
3510 self.rust_constructor_resource.remove(id);
3511 };
3512 if let Ok(id) = self
3513 .get_resource_index("SplitTime", &format!("MessageBox{}Animation", mb.name))
3514 {
3515 self.rust_constructor_resource.remove(id);
3516 };
3517 if !mb.keep_existing
3518 && let Ok(id) =
3519 self.get_resource_index("SplitTime", &format!("MessageBox{}", mb.name))
3520 {
3521 self.rust_constructor_resource.remove(id);
3522 };
3523 if let Ok(id) = self.get_resource_index("MessageBox", &mb.name) {
3524 self.rust_constructor_resource.remove(id);
3525 };
3526 } else {
3527 offset += mb.size[1] + 15_f32;
3528 };
3529 };
3530 }
3531 }
3532
3533 pub fn add_switch(&mut self, mut switch: Switch, image_name: &str, text_name: &str) {
3535 let mut count = 1;
3536 if switch.enable_hover_click_image[0] {
3537 count += 1;
3538 };
3539 if switch.enable_hover_click_image[1] {
3540 count += 1;
3541 };
3542 if switch.appearance.len() < count {
3543 self.problem_report(
3544 RustConstructorError::SwitchAppearanceMismatch {
3545 switch_name: switch.name.clone(),
3546 differ: count as u32 - switch.appearance.len() as u32,
3547 },
3548 SeverityLevel::SevereWarning,
3549 );
3550 return;
3551 };
3552 for _ in 0..switch.appearance.len() % count {
3553 switch.appearance.pop();
3554 }
3555 switch.text_name = String::new();
3556 if let Ok(id) = self.get_resource_index("Image", image_name)
3557 && let RCR::Image(mut im) = self.rust_constructor_resource[id].clone()
3558 {
3559 im.use_overlay_color = true;
3560 switch.switch_image_name = image_name.to_string();
3561 if self.check_resource_exists("Text", text_name)
3562 && let Ok(id2) = self.get_resource_index("Text", text_name)
3563 && let RCR::Text(t) = &mut self.rust_constructor_resource[id2]
3564 {
3565 switch.text_name = text_name.to_string();
3566 switch.text_origin_position = t.origin_position;
3567 t.center_display = (HorizontalAlign::Center, VerticalAlign::Center);
3568 t.x_grid = [0, 0];
3569 t.y_grid = [0, 0];
3570 };
3571 self.rust_constructor_resource[id] = RCR::Image(im);
3572 };
3573 switch.hint_text_name = String::new();
3574 if switch
3575 .appearance
3576 .iter()
3577 .filter(|x| !x.hint_text.is_empty())
3578 .count()
3579 > 0
3580 {
3581 switch.hint_text_name = format!("{}Hint", switch.name);
3582 self.add_text(
3583 Text::default()
3584 .name(&format!("{}Hint", switch.name))
3585 .content("")
3586 .origin_position(0_f32, 0_f32)
3587 .font_size(25_f32)
3588 .wrap_width(300_f32)
3589 .background_rounding(10_f32)
3590 .color(255, 255, 255, 0)
3591 .background_color(0, 0, 0, 0)
3592 .center_display(HorizontalAlign::Left, VerticalAlign::Top)
3593 .selectable(false),
3594 );
3595 self.add_split_time(
3596 SplitTime::default().name(&format!("{}StartHoverTime", switch.name)),
3597 );
3598 self.add_split_time(
3599 SplitTime::default().name(&format!("{}HintFadeAnimation", switch.name)),
3600 );
3601 };
3602 switch.state = 0;
3603 switch.animation_count = count as u32;
3604 self.rust_constructor_resource.push(RCR::Switch(switch));
3605 }
3606
3607 pub fn switch(
3609 &mut self,
3610 name: &str,
3611 ui: &mut Ui,
3612 ctx: &egui::Context,
3613 enable: bool,
3614 play_sound: bool,
3615 ) -> Result<[usize; 2], RustConstructorError> {
3616 let mut activated = [5, 0];
3617 let mut appearance_count = 0;
3618 if let Ok(id) = self.get_resource_index("Switch", name) {
3619 if let RCR::Switch(mut s) = self.rust_constructor_resource[id].clone() {
3620 if let Ok(id2) = self.get_resource_index("Image", &s.switch_image_name.clone()) {
3621 if let RCR::Image(mut im) = self.rust_constructor_resource[id2].clone() {
3622 s.reg_render_resource(&mut self.render_resource_list);
3623 let rect = Rect::from_min_size(
3624 Pos2::new(im.position[0], im.position[1]),
3625 Vec2::new(im.size[0], im.size[1]),
3626 );
3627 let mut hovered = false;
3628 if enable {
3629 if let Some(mouse_pos) = ui.input(|i| i.pointer.hover_pos()) {
3630 if rect.contains(mouse_pos) {
3632 if !s.hint_text_name.is_empty()
3633 && let Ok(id3) =
3634 self.get_resource_index("Text", &s.hint_text_name)
3635 && let RCR::Text(mut t) =
3636 self.rust_constructor_resource[id3].clone()
3637 {
3638 if !s.last_time_hovered {
3639 self.reset_split_time(&format!(
3640 "{}StartHoverTime",
3641 s.name
3642 ));
3643 } else if self.timer.total_time
3644 - self
3645 .split_time(&format!("{}StartHoverTime", s.name))
3646 .unwrap()[1]
3647 >= 2_f32
3648 || t.color[3] != 0
3649 {
3650 t.color[3] = 255;
3651 t.origin_position = [mouse_pos.x, mouse_pos.y];
3652 };
3653 t.center_display.0 = if mouse_pos.x
3654 + self.get_text_size(&s.hint_text_name, ui).unwrap()[0]
3655 <= ctx.available_rect().width()
3656 {
3657 HorizontalAlign::Left
3658 } else {
3659 HorizontalAlign::Right
3660 };
3661 t.center_display.1 = if mouse_pos.y
3662 + self.get_text_size(&s.hint_text_name, ui).unwrap()[1]
3663 <= ctx.available_rect().height()
3664 {
3665 VerticalAlign::Top
3666 } else {
3667 VerticalAlign::Bottom
3668 };
3669 self.rust_constructor_resource[id3] = RCR::Text(t);
3670 };
3671 hovered = true;
3672 let mut clicked = vec![];
3673 let mut active = false;
3674 for u in 0..s.click_method.len() as u32 {
3675 clicked.push(ui.input(|i| {
3676 i.pointer.button_down(
3677 s.click_method[u as usize].click_method,
3678 )
3679 }));
3680 if clicked[u as usize] {
3681 active = true;
3682 s.last_time_clicked_index = u as usize;
3683 break;
3684 };
3685 }
3686 if active {
3687 s.last_time_clicked = true;
3688 if s.enable_hover_click_image[1] {
3689 if s.enable_hover_click_image[0] {
3690 appearance_count = 2;
3691 } else {
3692 appearance_count = 1;
3693 };
3694 } else if !s.enable_hover_click_image[0] {
3695 appearance_count = 0;
3696 };
3697 } else {
3698 if s.last_time_clicked {
3699 if play_sound {
3700 general_click_feedback(&s.sound_path);
3701 };
3702 let mut count = 1;
3703 if s.enable_hover_click_image[0] {
3704 count += 1;
3705 };
3706 if s.enable_hover_click_image[1] {
3707 count += 1;
3708 };
3709 if s.click_method[s.last_time_clicked_index].action {
3710 if s.state < (s.appearance.len() / count - 1) as u32
3711 {
3712 s.state += 1;
3713 } else {
3714 s.state = 0;
3715 };
3716 };
3717 activated[0] = s.last_time_clicked_index;
3718 s.last_time_clicked = false;
3719 };
3720 if s.enable_hover_click_image[0] {
3721 appearance_count = 1;
3722 } else {
3723 appearance_count = 0;
3724 };
3725 };
3726 } else {
3727 s.last_time_clicked = false;
3728 appearance_count = 0;
3729 };
3730 } else {
3731 s.last_time_clicked = false;
3732 appearance_count = 0;
3733 };
3734 } else {
3735 s.last_time_clicked = false;
3736 appearance_count = 0;
3737 };
3738 if !hovered && !s.hint_text_name.is_empty() {
3739 if s.last_time_hovered {
3740 self.reset_split_time(&format!("{}HintFadeAnimation", s.name));
3741 };
3742 if let Ok(id3) = self.get_resource_index("Text", &s.hint_text_name)
3743 && let RCR::Text(mut t) =
3744 self.rust_constructor_resource[id3].clone()
3745 {
3746 if self.timer.total_time
3747 - self
3748 .split_time(&format!("{}HintFadeAnimation", s.name))
3749 .unwrap()[1]
3750 >= self.vertrefresh
3751 {
3752 self.reset_split_time(&format!("{}HintFadeAnimation", s.name));
3753 t.color[3] = t.color[3].saturating_sub(10);
3754 };
3755 self.rust_constructor_resource[id3] = RCR::Text(t);
3756 };
3757 };
3758 im.overlay_color = s.appearance
3759 [(s.state * s.animation_count + appearance_count) as usize]
3760 .color;
3761 if let Ok(id4) = self.get_resource_index(
3762 "ImageTexture",
3763 &s.appearance
3764 [(s.state * s.animation_count + appearance_count) as usize]
3765 .texture
3766 .clone(),
3767 ) && let RCR::ImageTexture(it) =
3768 self.rust_constructor_resource[id4].clone()
3769 {
3770 im.texture = it.texture.clone();
3771 };
3772 if !s.hint_text_name.is_empty()
3773 && let Ok(id3) = self.get_resource_index("Text", &s.hint_text_name)
3774 && let RCR::Text(mut t) = self.rust_constructor_resource[id3].clone()
3775 {
3776 t.background_color[3] = t.color[3];
3777 t.content = s.appearance
3778 [(s.state * s.animation_count + appearance_count) as usize]
3779 .hint_text
3780 .clone();
3781 self.rust_constructor_resource[id3] = RCR::Text(t);
3782 };
3783 s.last_time_hovered = hovered;
3784 activated[1] = s.state as usize;
3785 self.rust_constructor_resource[id] = RCR::Switch(s.clone());
3786 self.rust_constructor_resource[id2] = RCR::Image(im.clone());
3787 self.image(ui, &s.switch_image_name.clone(), ctx);
3788 if self.check_resource_exists("Text", &s.text_name) {
3789 if let Ok(id4) = self.get_resource_index("Text", &s.text_name)
3790 && let RCR::Text(mut t2) =
3791 self.rust_constructor_resource[id4].clone()
3792 {
3793 t2.origin_position = [
3794 im.position[0] + s.text_origin_position[0],
3795 im.position[1] + s.text_origin_position[1],
3796 ];
3797 t2.content = s.appearance
3798 [(s.state * s.animation_count + appearance_count) as usize]
3799 .text
3800 .clone();
3801 self.rust_constructor_resource[id4] = RCR::Text(t2);
3802 };
3803 self.text(ui, &s.text_name, ctx);
3804 };
3805 if self.check_resource_exists("Text", &s.hint_text_name) {
3806 self.text(ui, &s.hint_text_name, ctx);
3807 };
3808 Ok(activated)
3809 } else {
3810 Err(RustConstructorError::ImageNotFound {
3812 image_name: s.switch_image_name,
3813 })
3814 }
3815 } else {
3816 self.problem_report(
3817 RustConstructorError::ImageNotFound {
3818 image_name: name.to_string(),
3819 },
3820 SeverityLevel::SevereWarning,
3821 );
3822 Err(RustConstructorError::ImageNotFound {
3823 image_name: s.switch_image_name,
3824 })
3825 }
3826 } else {
3827 Err(RustConstructorError::SwitchNotFound {
3829 switch_name: name.to_string(),
3830 })
3831 }
3832 } else {
3833 self.problem_report(
3834 RustConstructorError::SwitchNotFound {
3835 switch_name: name.to_string(),
3836 },
3837 SeverityLevel::SevereWarning,
3838 );
3839 Err(RustConstructorError::SwitchNotFound {
3840 switch_name: name.to_string(),
3841 })
3842 }
3843 }
3844}