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