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