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