rust_constructor/app.rs
1//! Main application struct containing all GUI resources and state management.
2//!
3//! 程序主体,包含所有GUI资源和状态管理。
4use crate::{
5 BasicFrontResource, BorderKind, DisplayInfo, HorizontalAlign, ListInfoDescribeMethod,
6 PositionSizeConfig, RenderConfig, RequestMethod, RequestType, RustConstructorError,
7 RustConstructorId, RustConstructorResource, RustConstructorResourceBox, Timer, VerticalAlign,
8 advance_front::{
9 Background, BackgroundType, ClickAim, CustomPanelLayout, PanelLocation, PanelMargin,
10 PanelStorage, ResourcePanel, ScrollBarDisplayMethod, ScrollLengthMethod, Switch,
11 SwitchData,
12 },
13 background::{PageData, SplitTime, Variable},
14 background_type_discern,
15 basic_front::{
16 CustomRect, DebugTextureHandle, HyperlinkSelectMethod, Image, ImageLoadMethod, ImageLoader,
17 LoadedImageData, Text,
18 },
19 build_id, ctx_adapter, downcast_resource, downcast_resource_mut, get_tag,
20 position_size_processor, type_processor,
21};
22#[cfg(feature = "bevy")]
23use bevy_asset::Asset;
24#[cfg(feature = "bevy")]
25use bevy_reflect::TypePath;
26#[cfg(feature = "bevy")]
27use egui_bevy::{
28 Color32, ColorImage, CornerRadius, CursorIcon, FontData, FontDefinitions, FontFamily, FontId,
29 Galley, Id, Image as Img, ImageSource, Key, OpenUrl, Pos2, Rect, Sense, Stroke, StrokeKind, Ui,
30 Vec2, epaint::textures::TextureOptions, text::CCursor,
31};
32#[cfg(feature = "standard")]
33use egui_standard::{
34 Color32, ColorImage, CornerRadius, CursorIcon, FontData, FontDefinitions, FontFamily, FontId,
35 Galley, Id, Image as Img, ImageSource, Key, OpenUrl, Pos2, Rect, Sense, Stroke, StrokeKind, Ui,
36 Vec2, epaint::textures::TextureOptions, text::CCursor,
37};
38use log::{error, info, warn};
39use std::{
40 char,
41 cmp::Ordering,
42 collections::HashMap,
43 fmt::Debug,
44 fs::read,
45 sync::{Arc, Mutex},
46 thread,
47 vec::Vec,
48};
49
50/// This struct serves as the central hub for the Rust Constructor framework.
51///
52/// 该结构体是Rust Constructor框架的中心枢纽。
53#[cfg_attr(feature = "standard", derive(Debug))]
54#[cfg_attr(feature = "bevy", derive(Debug, Asset, TypePath))]
55pub struct App {
56 /// Collection of all Rust Constructor resources with type-erased storage.
57 ///
58 /// 所有Rust Constructor资源的集合,使用类型擦除存储。
59 pub rust_constructor_resource: Vec<RustConstructorResourceBox>,
60
61 /// Refresh rate for resource updates in milliseconds.
62 ///
63 /// 资源更新的刷新率(毫秒)。
64 pub tick_interval: u128,
65
66 /// Name of the current active page.
67 ///
68 /// 当前活动页面的名称。
69 pub current_page: String,
70
71 /// Timer for tracking application runtime and page durations.
72 ///
73 /// 用于跟踪应用程序运行时间和页面持续时间的计时器。
74 pub timer: Timer,
75
76 /// Record of recent frame times for performance monitoring.
77 ///
78 /// 最近帧时间的记录,用于性能监控。
79 pub frame_times: Vec<u128>,
80
81 /// The time for rendering the previous frame in milliseconds.
82 ///
83 /// 渲染上一帧的时间(毫秒)。
84 pub last_frame_time: Option<u128>,
85
86 /// List of resource IDs that are basic front resources.
87 ///
88 /// 基本前端资源的资源ID列表。
89 ///
90 /// This list should not be modified manually.
91 ///
92 /// 此列表不应手动修改。
93 pub basic_front_resource_list: Vec<String>,
94
95 /// Rendering layer information: (resource_id, [position, size], ignore_render_layer).
96 ///
97 /// 渲染层级信息:(资源ID, [位置, 尺寸], 是否忽略渲染层级)。
98 pub render_layer: Vec<(RustConstructorId, [[f32; 2]; 2], bool)>,
99
100 /// List of currently active resources.
101 ///
102 /// 当前活动的资源列表。
103 pub active_list: Vec<(RustConstructorId, Option<RustConstructorId>)>,
104
105 /// Queue of resources to be rendered in the current frame.
106 ///
107 /// 要在当前帧中呈现的资源队列。
108 pub render_list: Vec<(RustConstructorId, Option<RustConstructorId>)>,
109
110 /// List the loaded fonts.
111 ///
112 /// 列出已加载的字体。
113 pub loaded_fonts: Vec<[String; 2]>,
114
115 /// List the fonts that are currently loading.
116 ///
117 /// 列出正在加载的字体。
118 pub loading_fonts: Vec<[String; 2]>,
119
120 /// Background image loading infrastructure.
121 ///
122 /// 后台图片加载基础设施。
123 pub image_loader: ImageLoader,
124}
125
126unsafe impl Send for App {}
127
128unsafe impl Sync for App {}
129
130impl Default for App {
131 fn default() -> Self {
132 info!("Rust Constructor v2.11.4 (https://github.com/ChepleBob30/Rust-Constructor)");
133 App {
134 rust_constructor_resource: Vec::new(),
135 tick_interval: 50,
136 current_page: String::new(),
137 timer: Timer::default(),
138 frame_times: Vec::new(),
139 last_frame_time: None,
140 basic_front_resource_list: vec![
141 String::from("Image"),
142 String::from("Text"),
143 String::from("CustomRect"),
144 ],
145 render_layer: Vec::new(),
146 active_list: Vec::new(),
147 render_list: Vec::new(),
148 loaded_fonts: Vec::new(),
149 loading_fonts: Vec::new(),
150 image_loader: ImageLoader {
151 completed: Arc::new(Mutex::new(HashMap::new())),
152 },
153 }
154 }
155}
156
157impl App {
158 #[inline]
159 pub fn tick_interval(mut self, tick_interval: u128) -> Self {
160 self.tick_interval = tick_interval;
161 self
162 }
163
164 #[inline]
165 pub fn current_page(mut self, current_page: &str) -> Self {
166 self.current_page = current_page.to_string();
167 self
168 }
169
170 /// Consume all completed background image loads and create egui textures.
171 ///
172 /// 消费所有已完成的后台图片加载结果并创建 egui 纹理。
173 pub fn process_completed_image_loads(&mut self, ui: &mut Ui) {
174 let completed: Vec<(String, LoadedImageData)> = {
175 let mut lock = self.image_loader.completed.lock().unwrap();
176 lock.drain().collect()
177 };
178 for (resource_name, loaded_data) in completed {
179 let id = build_id(resource_name, "Image");
180 if self.check_resource_exists(&id).is_none() {
181 continue;
182 }
183 let texture = ctx_adapter(ui).load_texture(
184 &id.name,
185 loaded_data.color_image,
186 TextureOptions::LINEAR,
187 );
188 let handle = DebugTextureHandle::new(&texture);
189 if let Ok(image) = self.get_resource_mut::<Image>(&id) {
190 image.texture = Some(handle);
191 info!("Loaded texture for image '{}'.", id.name);
192 }
193 }
194 }
195
196 /// Draws all resources in the rendering queue at once, discarding all return values.
197 ///
198 /// 一次性绘制渲染队列中的所有资源,会丢弃所有返回值。
199 ///
200 /// This method iterates through all resources in the render list and draws them.
201 /// It's not recommended for production use due to error handling limitations.
202 ///
203 /// 此方法遍历渲染列表中的所有资源并绘制它们。由于错误处理限制,不建议在生产环境中使用。
204 pub fn draw_resources(&mut self, ui: &mut Ui) {
205 for i in 0..self.render_list.len() {
206 let _ = self.draw_resource_by_index(ui, i);
207 }
208 }
209
210 /// Draws a specific resource by its index in the rendering queue.
211 ///
212 /// 根据资源在渲染队列中的索引值绘制特定资源。
213 ///
214 /// This method handles the rendering of different resource types including:
215 /// - Images with various loading methods and transformations
216 /// - Text with formatting, selection, and hyperlink support
217 /// - Custom rectangles with borders and styling
218 ///
219 /// 此方法处理不同类型资源的渲染,包括:
220 /// - 具有各种加载方法和变换的图像
221 /// - 具有格式设置、选择和超链接支持的文本
222 /// - 具有边框和样式的自定义矩形
223 pub fn draw_resource_by_index(
224 &mut self,
225 ui: &mut Ui,
226 index: usize,
227 ) -> Result<(), RustConstructorError> {
228 if let Some(render_resource) = self.render_list.clone().get(index) {
229 match &*render_resource.0.discern_type {
230 "Image" => {
231 let image =
232 self.get_resource::<Image>(&build_id(&render_resource.0.name, "Image"))?;
233 if image.display_info.enable {
234 let mut image = image.clone();
235 match image.image_load_method {
236 ImageLoadMethod::ByPath((ref path, flip)) => {
237 if *path != image.last_frame_path {
238 image.last_frame_path = path.clone();
239 let resource_name = render_resource.0.name.clone();
240 let path_clone = path.clone();
241 let flip_val = flip;
242 let completed_arc = Arc::clone(&self.image_loader.completed);
243 thread::spawn(move || {
244 const MAX_TEXTURE_SIDE: u32 = 8192;
245 match std::fs::read(&path_clone) {
246 Ok(bytes) => {
247 if let Ok(img) = image::load_from_memory(&bytes) {
248 let (w, h) = (img.width(), img.height());
249 let img = if w > MAX_TEXTURE_SIDE
250 || h > MAX_TEXTURE_SIDE
251 {
252 let scale = MAX_TEXTURE_SIDE as f64
253 / w.max(h) as f64;
254 let new_w =
255 (w as f64 * scale).round() as u32;
256 let new_h =
257 (h as f64 * scale).round() as u32;
258 img.resize(
259 new_w,
260 new_h,
261 image::imageops::FilterType::Triangle,
262 )
263 } else {
264 img
265 };
266 let color_data = match flip_val {
267 [true, true] => {
268 img.fliph().flipv().into_rgba8()
269 }
270 [true, false] => img.fliph().into_rgba8(),
271 [false, true] => img.flipv().into_rgba8(),
272 _ => img.into_rgba8(),
273 };
274 let color_image =
275 ColorImage::from_rgba_unmultiplied(
276 [
277 color_data.width() as usize,
278 color_data.height() as usize,
279 ],
280 &color_data.into_raw(),
281 );
282 completed_arc.lock().unwrap().insert(
283 resource_name,
284 LoadedImageData { color_image },
285 );
286 }
287 }
288 Err(e) => {
289 eprintln!(
290 "[ImageLoadFailed]draw_resource_by_index: Failed to load an image from the path '{path_clone}': {e}",
291 );
292 warn!(
293 "[ImageLoadFailed]draw_resource_by_index: Failed to load an image from the path '{path_clone}': {e}",
294 );
295 }
296 }
297 });
298 }
299 }
300 ImageLoadMethod::ByTexture(ref texture) => {
301 image.texture = Some(texture.clone());
302 }
303 };
304 if image.texture.is_none()
305 && let Some(loaded) = self
306 .image_loader
307 .completed
308 .lock()
309 .unwrap()
310 .remove(&render_resource.0.name)
311 {
312 let texture = ctx_adapter(ui).load_texture(
313 &render_resource.0.name,
314 loaded.color_image,
315 TextureOptions::LINEAR,
316 );
317 image.texture = Some(DebugTextureHandle::new(&texture));
318 }
319 [image.position, image.size] = position_size_processor(
320 image.basic_front_resource_config.position_size_config,
321 ui,
322 );
323 if !image.display_info.hidden {
324 if let Some(clip_rect) = image.basic_front_resource_config.clip_rect {
325 let [min, size] = position_size_processor(clip_rect, ui);
326 ui.set_clip_rect(Rect::from_min_size(min.into(), size.into()));
327 };
328 if let Some(texture) = &image.texture {
329 let rect = Rect::from_min_size(
330 Pos2::new(image.position[0], image.position[1]),
331 Vec2::new(image.size[0], image.size[1]),
332 );
333
334 // 直接绘制图片
335 Img::new(ImageSource::Texture((&texture.0).into()))
336 .tint(Color32::from_rgba_unmultiplied(
337 image.overlay_color[0],
338 image.overlay_color[1],
339 image.overlay_color[2],
340 (image.alpha as f32 * image.overlay_alpha as f32 / 255_f32)
341 as u8,
342 ))
343 .bg_fill(Color32::from_rgba_unmultiplied(
344 image.background_color[0],
345 image.background_color[1],
346 image.background_color[2],
347 (image.alpha as f32 * image.background_alpha as f32
348 / 255_f32)
349 as u8,
350 ))
351 .rotate(
352 image.rotate_angle,
353 [
354 image.rotate_center[0] / image.size[0],
355 image.rotate_center[1] / image.size[1],
356 ]
357 .into(),
358 )
359 .paint_at(ui, rect)
360 };
361 if image.basic_front_resource_config.clip_rect.is_some() {
362 ui.set_clip_rect(Rect::from_min_size(
363 [0_f32, 0_f32].into(),
364 [ui.available_width(), ui.available_height()].into(),
365 ));
366 };
367 };
368 match image.image_load_method {
369 ImageLoadMethod::ByPath((ref path, _)) => {
370 image.last_frame_path = path.clone()
371 }
372 ImageLoadMethod::ByTexture(_) => {}
373 };
374 self.replace_resource(&render_resource.0.name, image)?;
375 };
376 }
377 "Text" => {
378 let text =
379 self.get_resource::<Text>(&build_id(&render_resource.0.name, "Text"))?;
380 if text.display_info.enable {
381 let mut text = text.clone();
382 [_, text.truncate_size] = position_size_processor(
383 text.basic_front_resource_config.position_size_config,
384 ui,
385 );
386 let display_content = if text.content.is_empty()
387 || text
388 .basic_front_resource_config
389 .position_size_config
390 .origin_size
391 .contains(&0_f32)
392 {
393 "".to_string()
394 } else {
395 let original_galley = ui.fonts_mut(|f| {
396 f.layout(
397 text.content.to_string(),
398 FontId::proportional(text.font_size),
399 Color32::default(),
400 text.truncate_size[0],
401 )
402 });
403
404 let mut truncated = text.content.to_string();
405 let mut ellipsis = "";
406 if original_galley.size().y > text.truncate_size[1] {
407 // 如果超出,逐步缩短文本直到加上省略号后能放下
408 ellipsis = "...";
409
410 while !truncated.is_empty() {
411 let test_text = format!("{}{}", truncated, ellipsis);
412 let test_galley = ui.fonts_mut(|f| {
413 f.layout(
414 test_text,
415 FontId::proportional(text.font_size),
416 Color32::default(),
417 text.truncate_size[0],
418 )
419 });
420
421 if test_galley.size().y <= text.truncate_size[1] {
422 break;
423 }
424
425 // 移除最后一个字符
426 truncated.pop();
427 }
428 };
429 format!("{}{}", truncated, ellipsis)
430 };
431 // 计算文本大小
432 let galley: Arc<Galley> = ui.fonts_mut(|f| {
433 f.layout(
434 display_content.to_string(),
435 if !text.font.is_empty() {
436 if self.loaded_fonts.iter().any(|x| x[0] == text.font) {
437 FontId::new(
438 text.font_size,
439 FontFamily::Name(text.font.clone().into()),
440 )
441 } else {
442 FontId::proportional(text.font_size)
443 }
444 } else {
445 FontId::proportional(text.font_size)
446 },
447 Color32::from_rgba_unmultiplied(
448 text.color[0],
449 text.color[1],
450 text.color[2],
451 text.alpha,
452 ),
453 text.truncate_size[0],
454 )
455 });
456 text.size = [
457 if text.auto_fit[0] {
458 galley.size().x
459 } else {
460 text.truncate_size[0]
461 },
462 if text.auto_fit[1] {
463 galley.size().y
464 } else {
465 text.truncate_size[1]
466 },
467 ];
468 text.actual_size = [galley.size().x, galley.size().y];
469 [text.position, _] = position_size_processor(
470 text.basic_front_resource_config
471 .position_size_config
472 .x_size_grid(0_f32, 0_f32)
473 .y_size_grid(0_f32, 0_f32)
474 .origin_size(text.size[0], text.size[1]),
475 ui,
476 );
477 // 查找超链接索引值
478 if text.last_frame_content != display_content {
479 text.hyperlink_index.clear();
480
481 // 创建字节索引到字符索引的映射
482 let byte_to_char_map: std::collections::HashMap<usize, usize> =
483 display_content
484 .char_indices()
485 .enumerate()
486 .map(|(char_idx, (byte_idx, _))| (byte_idx, char_idx))
487 .collect();
488
489 for (hyperlink_text, method) in &text.hyperlink_text {
490 let matches: Vec<(usize, &str)> =
491 display_content.match_indices(hyperlink_text).collect();
492 let text_char_count = hyperlink_text.chars().count();
493
494 if let HyperlinkSelectMethod::All(url) = method {
495 for (byte_index, _) in matches {
496 if let Some(&start_char_index) =
497 byte_to_char_map.get(&byte_index)
498 {
499 text.hyperlink_index.push((
500 start_char_index,
501 start_char_index + text_char_count,
502 url.clone(),
503 ));
504 };
505 }
506 } else if let HyperlinkSelectMethod::Segment(list) = method {
507 for (index, url) in list {
508 if *index >= matches.len() {
509 continue;
510 };
511 let (byte_index, _) = matches[*index];
512 if let Some(&start_char_index) =
513 byte_to_char_map.get(&byte_index)
514 {
515 text.hyperlink_index.push((
516 start_char_index,
517 start_char_index + text_char_count,
518 url.clone(),
519 ));
520 };
521 }
522 };
523 }
524 };
525 if !text.display_info.hidden {
526 // 使用绝对定位放置文本
527 let rect =
528 Rect::from_min_size(text.position.into(), text.actual_size.into());
529 // 绘制背景颜色
530 ui.painter().rect_filled(
531 rect,
532 text.background_rounding,
533 Color32::from_rgba_unmultiplied(
534 text.background_color[0],
535 text.background_color[1],
536 text.background_color[2],
537 text.background_alpha,
538 ),
539 );
540
541 if let Some(clip_rect) = text.basic_front_resource_config.clip_rect {
542 let [min, size] = position_size_processor(clip_rect, ui);
543 ui.set_clip_rect(Rect::from_min_size(min.into(), size.into()));
544 };
545
546 // 绘制文本
547 ui.painter().galley(
548 text.position.into(),
549 galley.clone(),
550 Color32::from_rgba_unmultiplied(
551 text.color[0],
552 text.color[1],
553 text.color[2],
554 text.alpha,
555 ),
556 );
557
558 // 绘制超链接
559 for (start, end, _) in &text.hyperlink_index {
560 // 获取超链接文本的范围
561 let start_cursor = galley.pos_from_cursor(CCursor::new(*start));
562 let end_cursor = galley.pos_from_cursor(CCursor::new(*end));
563
564 let start_pos = start_cursor.left_top();
565 let end_pos = end_cursor.right_top();
566 // 绘制超链接下划线
567 // 检查超链接是否跨行
568 if start_cursor.min.y == end_cursor.min.y {
569 // 单行超链接
570 let underline_y = text.position[1]
571 + start_pos.y
572 + galley.rows.first().map_or(14.0, |row| row.height())
573 - 2.0;
574
575 // 绘制下划线
576 let color = Color32::from_rgba_unmultiplied(
577 text.color[0],
578 text.color[1],
579 text.color[2],
580 text.alpha,
581 );
582
583 ui.painter().line_segment(
584 [
585 Pos2::new(text.position[0] + start_pos.x, underline_y),
586 Pos2::new(text.position[0] + end_pos.x, underline_y),
587 ],
588 Stroke::new(text.font_size / 10_f32, color),
589 );
590 } else {
591 // 多行超链接
592 let row_height =
593 galley.rows.first().map_or(14.0, |row| row.height()); // 默认行高14.0
594
595 // 计算起始行和结束行的索引
596 let start_row = (start_pos.y / row_height).round() as usize;
597 let end_row = (end_pos.y / row_height).round() as usize;
598
599 for row in start_row..=end_row {
600 let row_y =
601 text.position[1] + row as f32 * row_height + row_height
602 - 2.0; // 行底部稍微上移一点绘制下划线
603
604 // 获取当前行的矩形范围
605 if let Some(current_row) = galley.rows.get(row) {
606 let row_rect = current_row.rect();
607
608 let color = Color32::from_rgba_unmultiplied(
609 text.color[0],
610 text.color[1],
611 text.color[2],
612 text.alpha,
613 );
614
615 if row == start_row {
616 // 第一行从文本开始位置到行尾
617 ui.painter().line_segment(
618 [
619 Pos2::new(
620 text.position[0] + start_pos.x,
621 row_y,
622 ),
623 Pos2::new(
624 text.position[0] + row_rect.max.x,
625 row_y,
626 ),
627 ],
628 Stroke::new(text.font_size / 10_f32, color),
629 );
630 } else if row == end_row {
631 // 最后一行从行首到文本结束位置
632 ui.painter().line_segment(
633 [
634 Pos2::new(
635 text.position[0] + row_rect.min.x,
636 row_y,
637 ),
638 Pos2::new(
639 text.position[0] + end_pos.x,
640 row_y,
641 ),
642 ],
643 Stroke::new(text.font_size / 10_f32, color),
644 );
645 } else {
646 // 中间整行下划线
647 ui.painter().line_segment(
648 [
649 Pos2::new(
650 text.position[0] + row_rect.min.x,
651 row_y,
652 ),
653 Pos2::new(
654 text.position[0] + row_rect.max.x,
655 row_y,
656 ),
657 ],
658 Stroke::new(text.font_size / 10_f32, color),
659 );
660 };
661 };
662 }
663 };
664 }
665
666 if text.selectable {
667 // 处理选择逻辑
668 let cursor_at_pointer = |pointer_pos: Vec2| -> usize {
669 let relative_pos = pointer_pos - text.position.into();
670 let cursor = galley.cursor_from_pos(relative_pos);
671 cursor.index
672 };
673
674 let fullscreen_detect_result = ui.input(|i| i.pointer.clone());
675 let rect = Rect::from_min_size(
676 text.position.into(),
677 text.actual_size.into(),
678 );
679 let detect_result = ui.interact(
680 rect,
681 Id::new(&render_resource.0.name),
682 Sense::click_and_drag(),
683 );
684
685 if !detect_result.clicked()
686 && (fullscreen_detect_result.any_click()
687 || fullscreen_detect_result.any_pressed())
688 {
689 text.selection = None;
690 };
691
692 if let Some(index) = self.get_render_layer_resource(&build_id(
693 &render_resource.0.name,
694 "Text",
695 )) && let Some(mouse_pos) =
696 fullscreen_detect_result.interact_pos()
697 && self.resource_get_focus(
698 index,
699 mouse_pos.into(),
700 false,
701 vec![],
702 )
703 && (detect_result.clicked() || detect_result.drag_started())
704 {
705 let cursor = cursor_at_pointer(mouse_pos.to_vec2());
706 text.selection = Some((cursor, cursor));
707 };
708
709 if detect_result.dragged()
710 && text.selection.is_some()
711 && let Some(pointer_pos) =
712 ui.input(|i| i.pointer.interact_pos())
713 {
714 let cursor = cursor_at_pointer(pointer_pos.to_vec2());
715 if let Some((start, _)) = text.selection {
716 text.selection = Some((start, cursor));
717 };
718 };
719
720 if text.selection.is_some()
721 && ui.input(|input| {
722 input.key_released(Key::A) && input.modifiers.command
723 })
724 {
725 text.selection = Some((0, display_content.chars().count()));
726 };
727
728 // 处理复制操作
729 let copy_triggered = ui.input(|input| {
730 let c_released = input.key_released(Key::C);
731 let cmd_pressed = input.modifiers.command;
732 c_released && cmd_pressed
733 });
734 if copy_triggered && let Some((start, end)) = text.selection {
735 let (start, end) = (start.min(end), start.max(end));
736 let chars: Vec<char> = display_content.chars().collect();
737 if start <= chars.len() && end <= chars.len() && start < end {
738 let selected_text: String =
739 chars[start..end].iter().collect();
740 ctx_adapter(ui).copy_text(selected_text);
741 };
742 };
743
744 // 绘制选择区域背景
745 if let Some((start, end)) = text.selection {
746 let (start, end) = (start.min(end), start.max(end));
747 if start != end {
748 // 获取选择区域的范围
749 let start_cursor =
750 galley.pos_from_cursor(CCursor::new(start));
751 let end_cursor = galley.pos_from_cursor(CCursor::new(end));
752
753 let start_pos = start_cursor.left_top();
754 let end_pos = end_cursor.right_top();
755 // 选择框绘制
756 if start_pos.y == end_pos.y {
757 // 单行选择
758 let rows = &galley.rows;
759 let row_height = if !rows.is_empty() {
760 // 获取实际行的高度
761 if let Some(row) = rows.first() {
762 row.height()
763 } else {
764 text.actual_size[1]
765 / display_content.lines().count() as f32
766 }
767 } else {
768 text.actual_size[1]
769 / display_content.lines().count() as f32
770 };
771
772 let selection_rect = Rect::from_min_max(
773 Pos2::new(
774 text.position[0] + start_pos.x,
775 text.position[1] + start_pos.y,
776 ),
777 Pos2::new(
778 text.position[0] + end_pos.x,
779 text.position[1] + start_pos.y + row_height,
780 ),
781 );
782 ui.painter().rect_filled(
783 selection_rect,
784 0.0,
785 Color32::from_rgba_unmultiplied(0, 120, 255, 100),
786 );
787 } else {
788 // 多行选择 - 为每行创建精确的矩形
789 let rows = &galley.rows;
790 let row_height = if !rows.is_empty() {
791 rows[0].height()
792 } else {
793 text.actual_size[1]
794 / display_content.lines().count() as f32
795 };
796
797 // 计算选择的上下边界
798 let selection_top =
799 text.position[1] + start_pos.y.min(end_pos.y);
800 let selection_bottom =
801 text.position[1] + start_pos.y.max(end_pos.y);
802
803 // 确定起始行和结束行的索引
804 let start_row_index =
805 (start_pos.y / row_height).floor() as usize;
806 let end_row_index =
807 (end_pos.y / row_height).floor() as usize;
808 let (first_row_index, last_row_index) =
809 if start_row_index <= end_row_index {
810 (start_row_index, end_row_index)
811 } else {
812 (end_row_index, start_row_index)
813 };
814
815 for (i, row) in rows.iter().enumerate() {
816 let row_y =
817 text.position[1] + row_height * i as f32;
818 let row_bottom = row_y + row_height;
819 // 检查当前行是否与选择区域相交
820 if row_bottom > selection_top
821 && row_y <= selection_bottom
822 {
823 let left = if i == first_row_index {
824 // 首行 - 从选择开始位置开始
825 text.position[0] + start_pos.x
826 } else {
827 // 非首行 - 从行首开始
828 text.position[0] + row.rect().min.x
829 };
830
831 let right = if i == last_row_index {
832 // 尾行 - 到选择结束位置结束
833 text.position[0] + end_pos.x
834 } else {
835 // 非尾行 - 到行尾结束
836 text.position[0] + row.rect().max.x
837 };
838
839 let selection_rect = Rect::from_min_max(
840 Pos2::new(left, row_y),
841 Pos2::new(right, row_bottom),
842 );
843
844 // 确保矩形有效
845 if selection_rect.width() > 0.0
846 && selection_rect.height() > 0.0
847 {
848 ui.painter().rect_filled(
849 selection_rect,
850 0.0,
851 Color32::from_rgba_unmultiplied(
852 0, 120, 255, 100,
853 ),
854 );
855 };
856 };
857 }
858 };
859 };
860 };
861 };
862
863 // 处理超链接操作
864 for (start, end, url) in &text.hyperlink_index {
865 // 获取超链接文本的范围
866 let start_cursor = galley.pos_from_cursor(CCursor::new(*start));
867 let end_cursor = galley.pos_from_cursor(CCursor::new(*end));
868
869 let start_pos = start_cursor.left_top();
870 let end_pos = end_cursor.right_top();
871
872 let row_height =
873 galley.rows.first().map_or(14.0, |row| row.height());
874
875 // 为超链接创建交互响应对象
876 let link_responses = if start_cursor.min.y == end_cursor.min.y {
877 // 单行超链接
878 let link_rect = Rect::from_min_max(
879 Pos2::new(
880 text.position[0] + start_pos.x,
881 text.position[1] + start_pos.y,
882 ),
883 Pos2::new(
884 text.position[0] + end_pos.x,
885 text.position[1] + start_pos.y + row_height,
886 ),
887 );
888 vec![ui.interact(
889 link_rect,
890 Id::new(format!(
891 "link_{}_{}_{}",
892 render_resource.0.name, start, end
893 )),
894 Sense::click(),
895 )]
896 } else {
897 // 多行超链接
898 let start_row = (start_pos.y / row_height).round() as usize;
899 let end_row = (end_pos.y / row_height).round() as usize;
900 let mut responses = Vec::new();
901
902 for row in start_row..=end_row {
903 if let Some(current_row) = galley.rows.get(row) {
904 let row_rect = current_row.rect();
905 let row_y = text.position[1] + row as f32 * row_height;
906
907 let link_rect = if row == start_row {
908 // 第一行从文本开始位置到行尾
909 Rect::from_min_max(
910 Pos2::new(
911 text.position[0] + start_pos.x,
912 row_y,
913 ),
914 Pos2::new(
915 text.position[0] + row_rect.max.x,
916 row_y + row_height,
917 ),
918 )
919 } else if row == end_row {
920 // 最后一行从行首到文本结束位置
921 Rect::from_min_max(
922 Pos2::new(
923 text.position[0] + row_rect.min.x,
924 row_y,
925 ),
926 Pos2::new(
927 text.position[0] + end_pos.x,
928 row_y + row_height,
929 ),
930 )
931 } else {
932 // 中间整行
933 Rect::from_min_max(
934 Pos2::new(
935 text.position[0] + row_rect.min.x,
936 row_y,
937 ),
938 Pos2::new(
939 text.position[0] + row_rect.max.x,
940 row_y + row_height,
941 ),
942 )
943 };
944
945 responses.push(ui.interact(
946 link_rect,
947 Id::new(format!(
948 "link_{}_{}_{}_row_{}",
949 render_resource.0.name, start, end, row
950 )),
951 Sense::click(),
952 ));
953 };
954 }
955 responses
956 };
957
958 // 检查是否正在点击这个超链接
959 let mut is_pressing_link = false;
960 for link_response in &link_responses {
961 if let Some(index) = self.get_render_layer_resource(&build_id(
962 &render_resource.0.name,
963 "Text",
964 )) && let Some(mouse_pos) =
965 ui.input(|i| i.pointer.interact_pos())
966 && self.resource_get_focus(
967 index,
968 mouse_pos.into(),
969 false,
970 vec![],
971 )
972 {
973 if link_response.is_pointer_button_down_on()
974 && !link_response.drag_started()
975 {
976 text.selection = None;
977 if let Some(pointer_pos) =
978 ui.input(|i| i.pointer.interact_pos())
979 {
980 let relative_pos = pointer_pos
981 - <[f32; 2] as Into<Pos2>>::into(text.position);
982 let cursor = galley.cursor_from_pos(relative_pos);
983 if cursor.index >= *start && cursor.index <= *end {
984 is_pressing_link = true;
985 break;
986 };
987 };
988 };
989 // 检查是否释放了鼠标(点击完成)
990 let mut clicked_on_link = false;
991 for link_response in &link_responses {
992 if link_response.clicked()
993 && let Some(pointer_pos) =
994 ui.input(|i| i.pointer.interact_pos())
995 {
996 let relative_pos = pointer_pos
997 - <[f32; 2] as Into<Pos2>>::into(text.position);
998 let cursor = galley.cursor_from_pos(relative_pos);
999 if cursor.index >= *start && cursor.index <= *end {
1000 clicked_on_link = true;
1001 break;
1002 };
1003 };
1004 }
1005
1006 if clicked_on_link {
1007 // 执行超链接跳转
1008 if !url.is_empty() {
1009 ctx_adapter(ui).open_url(OpenUrl::new_tab(url));
1010 };
1011 };
1012 };
1013 }
1014
1015 // 绘制超链接高亮(如果正在点击或悬停)
1016 if is_pressing_link {
1017 if start_cursor.min.y == end_cursor.min.y {
1018 // 单行超链接高亮
1019 let selection_rect = Rect::from_min_max(
1020 Pos2::new(
1021 text.position[0] + start_pos.x,
1022 text.position[1] + start_pos.y,
1023 ),
1024 Pos2::new(
1025 text.position[0] + end_pos.x,
1026 text.position[1]
1027 + start_pos.y
1028 + galley
1029 .rows
1030 .first()
1031 .map_or(14.0, |row| row.height()),
1032 ),
1033 );
1034 ui.painter().rect_filled(
1035 selection_rect,
1036 0.0,
1037 Color32::from_rgba_unmultiplied(0, 120, 255, 100),
1038 );
1039 } else {
1040 // 多行超链接高亮
1041 let row_height =
1042 galley.rows.first().map_or(14.0, |row| row.height());
1043 let start_row = (start_pos.y / row_height).round() as usize;
1044 let end_row = (end_pos.y / row_height).round() as usize;
1045
1046 for row in start_row..=end_row {
1047 if let Some(current_row) = galley.rows.get(row) {
1048 let row_rect = current_row.rect();
1049
1050 if row == start_row {
1051 // 第一行从文本开始位置到行尾
1052 let selection_rect = Rect::from_min_max(
1053 Pos2::new(
1054 text.position[0] + start_pos.x,
1055 text.position[1]
1056 + row as f32 * row_height,
1057 ),
1058 Pos2::new(
1059 text.position[0] + row_rect.max.x,
1060 text.position[1]
1061 + row as f32 * row_height
1062 + row_height,
1063 ),
1064 );
1065 ui.painter().rect_filled(
1066 selection_rect,
1067 0.0,
1068 Color32::from_rgba_unmultiplied(
1069 0, 120, 255, 100,
1070 ),
1071 );
1072 } else if row == end_row {
1073 // 最后一行从行首到文本结束位置
1074 let selection_rect = Rect::from_min_max(
1075 Pos2::new(
1076 text.position[0] + row_rect.min.x,
1077 text.position[1]
1078 + row as f32 * row_height,
1079 ),
1080 Pos2::new(
1081 text.position[0] + end_pos.x,
1082 text.position[1]
1083 + row as f32 * row_height
1084 + row_height,
1085 ),
1086 );
1087 ui.painter().rect_filled(
1088 selection_rect,
1089 0.0,
1090 Color32::from_rgba_unmultiplied(
1091 0, 120, 255, 100,
1092 ),
1093 );
1094 } else {
1095 // 中间整行高亮
1096 let selection_rect = Rect::from_min_max(
1097 Pos2::new(
1098 text.position[0] + row_rect.min.x,
1099 text.position[1]
1100 + row as f32 * row_height,
1101 ),
1102 Pos2::new(
1103 text.position[0] + row_rect.max.x,
1104 text.position[1]
1105 + row as f32 * row_height
1106 + row_height,
1107 ),
1108 );
1109 ui.painter().rect_filled(
1110 selection_rect,
1111 0.0,
1112 Color32::from_rgba_unmultiplied(
1113 0, 120, 255, 100,
1114 ),
1115 );
1116 };
1117 };
1118 }
1119 };
1120 };
1121 }
1122 if text.basic_front_resource_config.clip_rect.is_some() {
1123 ui.set_clip_rect(Rect::from_min_size(
1124 [0_f32, 0_f32].into(),
1125 [ui.available_width(), ui.available_height()].into(),
1126 ));
1127 };
1128 } else {
1129 text.selection = None;
1130 };
1131 text.last_frame_content = display_content;
1132 self.replace_resource(&render_resource.0.name, text)?;
1133 };
1134 }
1135 "CustomRect" => {
1136 let custom_rect = self.get_resource::<CustomRect>(&build_id(
1137 &render_resource.0.name,
1138 "CustomRect",
1139 ))?;
1140 if custom_rect.display_info.enable {
1141 let mut custom_rect = custom_rect.clone();
1142 [custom_rect.position, custom_rect.size] = position_size_processor(
1143 custom_rect.basic_front_resource_config.position_size_config,
1144 ui,
1145 );
1146 if !custom_rect.display_info.hidden {
1147 if let Some(clip_rect) =
1148 custom_rect.basic_front_resource_config.clip_rect
1149 {
1150 let [min, size] = position_size_processor(clip_rect, ui);
1151 ui.set_clip_rect(Rect::from_min_size(min.into(), size.into()));
1152 };
1153 ui.painter().rect(
1154 Rect::from_min_max(
1155 Pos2::new(custom_rect.position[0], custom_rect.position[1]),
1156 Pos2::new(
1157 custom_rect.position[0] + custom_rect.size[0],
1158 custom_rect.position[1] + custom_rect.size[1],
1159 ),
1160 ),
1161 custom_rect.rounding,
1162 if let Some(overlay_alpha) = custom_rect.overlay_alpha {
1163 Color32::from_rgba_unmultiplied(
1164 (custom_rect.color[0] as f32
1165 * custom_rect.overlay_color[0] as f32
1166 / 255_f32)
1167 as u8,
1168 (custom_rect.color[1] as f32
1169 * custom_rect.overlay_color[1] as f32
1170 / 255_f32)
1171 as u8,
1172 (custom_rect.color[2] as f32
1173 * custom_rect.overlay_color[2] as f32
1174 / 255_f32)
1175 as u8,
1176 (custom_rect.alpha as f32 * overlay_alpha as f32 / 255_f32)
1177 as u8,
1178 )
1179 } else {
1180 Color32::from_rgba_unmultiplied(
1181 custom_rect.color[0],
1182 custom_rect.color[1],
1183 custom_rect.color[2],
1184 custom_rect.alpha,
1185 )
1186 },
1187 Stroke {
1188 width: custom_rect.border_width,
1189 color: if let Some(overlay_border_alpha) =
1190 custom_rect.overlay_border_alpha
1191 {
1192 Color32::from_rgba_unmultiplied(
1193 (custom_rect.border_color[0] as f32
1194 * custom_rect.overlay_border_color[0] as f32
1195 / 255_f32)
1196 as u8,
1197 (custom_rect.border_color[1] as f32
1198 * custom_rect.overlay_border_color[1] as f32
1199 / 255_f32)
1200 as u8,
1201 (custom_rect.border_color[2] as f32
1202 * custom_rect.overlay_border_color[2] as f32
1203 / 255_f32)
1204 as u8,
1205 (custom_rect.border_alpha as f32
1206 * overlay_border_alpha as f32
1207 / 255_f32)
1208 as u8,
1209 )
1210 } else {
1211 Color32::from_rgba_unmultiplied(
1212 custom_rect.border_color[0],
1213 custom_rect.border_color[1],
1214 custom_rect.border_color[2],
1215 custom_rect.border_alpha,
1216 )
1217 },
1218 },
1219 match custom_rect.border_kind {
1220 BorderKind::Inside => StrokeKind::Inside,
1221 BorderKind::Middle => StrokeKind::Middle,
1222 BorderKind::Outside => StrokeKind::Outside,
1223 },
1224 );
1225 if custom_rect.basic_front_resource_config.clip_rect.is_some() {
1226 ui.set_clip_rect(Rect::from_min_size(
1227 [0_f32, 0_f32].into(),
1228 [ui.available_width(), ui.available_height()].into(),
1229 ));
1230 };
1231 };
1232 self.replace_resource(&render_resource.0.name, custom_rect)?;
1233 };
1234 }
1235 _ => {
1236 unreachable!()
1237 }
1238 }
1239 Ok(())
1240 } else {
1241 error!(
1242 "[IndexOutOfRange]draw_resource_by_index: The maximum index of the target list is {}, but the index is {index}.",
1243 self.render_list.len() - 1
1244 );
1245 Err(RustConstructorError {
1246 error_id: "IndexOutOfRange".to_string(),
1247 description: format!(
1248 "The maximum index of the target list is {}, but the index is {index}.",
1249 self.render_list.len() - 1
1250 ),
1251 })
1252 }
1253 }
1254
1255 /// Generate information for Rust Constructor resources.
1256 ///
1257 /// 生成Rust Constructor资源的信息。
1258 ///
1259 /// This method returns a formatted string containing details about all resources.
1260 /// The level of detail depends on the specified method.
1261 ///
1262 /// 此方法返回一个格式化字符串,包含所有资源的详细信息。
1263 /// 详细程度取决于指定的方法。
1264 pub fn rust_constructor_resource_info(
1265 &self,
1266 describe: ListInfoDescribeMethod,
1267 print: bool,
1268 ) -> String {
1269 let mut text =
1270 String::from("————————————————————————————————————\nRust Constructor Resource Info:\n");
1271 for info in &self.rust_constructor_resource {
1272 if let ListInfoDescribeMethod::Detailed(format) = describe {
1273 text += &if format {
1274 format!(
1275 "\nName: {}\nType: {}\nDetail: {:#?}\n",
1276 info.id.name, info.id.discern_type, info.content,
1277 )
1278 } else {
1279 format!(
1280 "\nName: {}\nType: {}\nDetail: {:?}\n",
1281 info.id.name, info.id.discern_type, info.content,
1282 )
1283 };
1284 } else {
1285 text += &format!("\nName: {}\nType: {}\n", info.id.name, info.id.discern_type,)
1286 };
1287 }
1288 if print {
1289 println!("{text}");
1290 };
1291 text
1292 }
1293
1294 /// Generates information about currently active resources.
1295 ///
1296 /// 生成当前活跃资源的信息。
1297 ///
1298 /// This method returns a formatted string containing details about all resources
1299 /// in the active list. The level of detail depends on the specified method.
1300 ///
1301 /// 此方法返回一个格式化字符串,包含活动列表中所有资源的详细信息。
1302 /// 详细程度取决于指定的方法。
1303 pub fn active_list_info(&self, describe: ListInfoDescribeMethod, print: bool) -> String {
1304 let mut text =
1305 String::from("————————————————————————————————————\nResource Active Info:\n");
1306 for info in &self.active_list {
1307 if let ListInfoDescribeMethod::Detailed(format) = describe {
1308 if let Some(index) = self.check_resource_exists(&info.0) {
1309 text += &if format {
1310 format!(
1311 "\nName: {}\nType: {}\nCiter: {:?}\nDetail: {:#?}\n",
1312 info.0.name,
1313 info.0.discern_type,
1314 info.1,
1315 self.rust_constructor_resource[index],
1316 )
1317 } else {
1318 format!(
1319 "\nName: {}\nType: {}\nCiter: {:?}\nDetail: {:?}\n",
1320 info.0.name,
1321 info.0.discern_type,
1322 info.1,
1323 self.rust_constructor_resource[index],
1324 )
1325 };
1326 };
1327 } else {
1328 text += &format!(
1329 "\nName: {}\nType: {}\nCiter: {:?}\n",
1330 info.0.name, info.0.discern_type, info.1
1331 );
1332 };
1333 }
1334 if print {
1335 println!("{text}");
1336 };
1337 text
1338 }
1339
1340 /// Generates information about the current rendering layers.
1341 ///
1342 /// 生成当前渲染层级的信息。
1343 ///
1344 /// This method returns a formatted string containing details about the rendering
1345 /// layer stack, including resource positions and rendering behavior.
1346 ///
1347 /// 此方法返回一个格式化字符串,包含渲染层级堆栈的详细信息,
1348 /// 包括资源位置和渲染行为。
1349 pub fn render_layer_info(&self, print: bool) -> String {
1350 let mut text = String::from("————————————————————————————————————\nRender Layer Info:\n");
1351 for (
1352 RustConstructorId { name, discern_type },
1353 [min_position, max_position],
1354 ignore_render_layer,
1355 ) in &self.render_layer
1356 {
1357 text += &format!(
1358 "\nName: {}\nType: {}\nMin Position: {:?}\nMax Position: {:?}\nIgnore Render Layer: {}\n",
1359 name, discern_type, min_position, max_position, ignore_render_layer
1360 )
1361 }
1362 if print {
1363 println!("{text}");
1364 };
1365 text
1366 }
1367
1368 /// Generates information about the current render queue.
1369 ///
1370 /// 生成当前渲染队列的信息。
1371 ///
1372 /// This method returns a formatted string listing all resources in the
1373 /// render queue with their names and types.
1374 ///
1375 /// 此方法返回一个格式化字符串,列出渲染队列中的所有资源及其名称和类型。
1376 pub fn render_list_info(&self, print: bool) -> String {
1377 let mut text = String::from("————————————————————————————————————\nRender List Info:\n");
1378 for (RustConstructorId { name, discern_type }, citer) in &self.render_list {
1379 text += &format!(
1380 "\nName: {}\nType: {}\nCiter: {:?}\n",
1381 name, discern_type, citer
1382 )
1383 }
1384 if print {
1385 println!("{text}");
1386 };
1387 text
1388 }
1389
1390 /// Updates the render queue based on active resources.
1391 ///
1392 /// 根据活跃资源更新渲染队列。
1393 ///
1394 /// This method synchronizes the render list with the active list, ensuring that
1395 /// only active basic front resources are included in the rendering queue.
1396 ///
1397 /// 此方法将渲染列表与活跃列表同步,确保只有活跃的基本前端资源包含在渲染队列中。
1398 pub fn update_render_list(&mut self) {
1399 if self.render_list.is_empty() {
1400 for info in &self.active_list {
1401 if self
1402 .basic_front_resource_list
1403 .contains(&info.0.discern_type)
1404 {
1405 self.render_list.push(info.clone());
1406 };
1407 }
1408 } else {
1409 let mut count = 0;
1410 for render_resource in &self.render_list.clone() {
1411 if !self.active_list.contains(render_resource) {
1412 self.render_list.remove(count);
1413 } else {
1414 count += 1;
1415 };
1416 }
1417 let mut insert_index = 0;
1418 for info in &self.active_list {
1419 if self
1420 .basic_front_resource_list
1421 .contains(&info.0.discern_type)
1422 {
1423 if !self.render_list.contains(info) {
1424 self.render_list.insert(insert_index, info.clone());
1425 insert_index += 1;
1426 } else if self.render_list[insert_index].cmp(info) == Ordering::Equal {
1427 insert_index += 1;
1428 };
1429 };
1430 }
1431 };
1432 }
1433
1434 /// Attempts to move a resource to the front of the render queue, ignoring whether it exists.
1435 ///
1436 /// 请求在渲染队列中插队,且无视申请跳过队列的资源是否存在。
1437 ///
1438 /// This is a safe wrapper around `request_jump_render_list` that suppresses errors.
1439 /// Use when you want to attempt reordering without handling potential errors.
1440 ///
1441 /// 这是`request_jump_render_list`的安全包装器,会抑制错误。当您想要尝试重新排序而不处理潜在错误时使用。
1442 pub fn try_request_jump_render_list(
1443 &mut self,
1444 requester: RequestMethod,
1445 request_type: RequestType,
1446 ) {
1447 let _ = self.request_jump_render_list(requester, request_type);
1448 }
1449
1450 /// Moves a resource to the front of the render queue with error handling.
1451 ///
1452 /// 将资源移动到渲染队列的前面(含错误处理)。
1453 ///
1454 /// This method allows changing the rendering order of resources by moving a specific
1455 /// resource to the top of the queue or up a specified number of layers.
1456 ///
1457 /// 此方法允许通过将特定资源移动到队列顶部或上移指定层数来更改资源的渲染顺序。
1458 pub fn request_jump_render_list(
1459 &mut self,
1460 requester: RequestMethod,
1461 request_type: RequestType,
1462 ) -> Result<(), RustConstructorError> {
1463 match requester {
1464 RequestMethod::Id(id) => {
1465 if let Some(index) = self.render_list.iter().position(|x| x.0 == id) {
1466 self.jump_render_list_processor(index, request_type)?;
1467 Ok(())
1468 } else {
1469 error!(
1470 "[RenderResourceNotFound]request_jump_render_list: Render resource '{}({})' not found.",
1471 id.name, id.discern_type
1472 );
1473 Err(RustConstructorError {
1474 error_id: "RenderResourceNotFound".to_string(),
1475 description: format!(
1476 "Render resource '{}({})' not found.",
1477 id.name, id.discern_type
1478 ),
1479 })
1480 }
1481 }
1482 RequestMethod::Citer(citer) => {
1483 for (i, render_resource) in self.render_list.iter().enumerate() {
1484 if let Some(render_citer) = &render_resource.1
1485 && render_citer == &citer
1486 {
1487 self.jump_render_list_processor(i, request_type)?;
1488 return Ok(());
1489 };
1490 }
1491 error!(
1492 "[RenderResourceNotFound]request_jump_render_list: Render resource citer '{}({})' not found.",
1493 citer.name, citer.discern_type
1494 );
1495 Err(RustConstructorError {
1496 error_id: "RenderResourceNotFound".to_string(),
1497 description: format!(
1498 "Render resource citer '{}({})' not found.",
1499 citer.name, citer.discern_type
1500 ),
1501 })
1502 }
1503 }
1504 }
1505
1506 /// Handle the operation of skipping the rendering queue.
1507 ///
1508 /// 处理跳过渲染队列操作。
1509 pub fn jump_render_list_processor(
1510 &mut self,
1511 requester_index: usize,
1512 request_type: RequestType,
1513 ) -> Result<(), RustConstructorError> {
1514 if requester_index < self.render_list.len() {
1515 let requester = self.render_list.remove(requester_index);
1516 let new_index = match request_type {
1517 RequestType::Top => self.render_list.len(),
1518 RequestType::Up(up) => {
1519 if requester_index + up <= self.render_list.len() {
1520 requester_index + up
1521 } else {
1522 self.render_list.len()
1523 }
1524 }
1525 };
1526 self.render_list.insert(new_index, requester);
1527 Ok(())
1528 } else {
1529 error!(
1530 "[IndexOutOfRange]jump_render_list_processor: The maximum index of the target list is {}, but the index is {requester_index}.",
1531 self.render_list.len() - 1
1532 );
1533 Err(RustConstructorError {
1534 error_id: "IndexOutOfRange".to_string(),
1535 description: format!(
1536 "The maximum index of the target list is {}, but the index is {requester_index}.",
1537 self.render_list.len() - 1
1538 ),
1539 })
1540 }
1541 }
1542
1543 /// Updates the rendering layer information for all rendering resources.
1544 ///
1545 /// 更新所有渲染资源的渲染层信息。
1546 ///
1547 /// This method recalculates the rendering layer by processing all resources
1548 /// in the render list and updating their position, size, and rendering properties.
1549 ///
1550 /// 此方法通过处理渲染列表中的所有资源并更新它们的位置、尺寸和渲染属性来重新计算渲染层级。
1551 pub fn update_render_layer(&mut self, ui: &Ui) -> Result<(), RustConstructorError> {
1552 self.render_layer.clear();
1553 for info in &self.render_list {
1554 let basic_front_resource = self.get_basic_front_resource(&info.0)?;
1555 if let Some(display_info) = basic_front_resource.display_display_info() {
1556 self.render_layer.push((
1557 info.0.clone(),
1558 if let Some(clip_rect) = basic_front_resource
1559 .display_basic_front_resource_config()
1560 .clip_rect
1561 {
1562 let [position, size] = position_size_processor(clip_rect, ui);
1563 let [resource_rect, clip_rect] = [
1564 Rect::from_min_max(
1565 basic_front_resource.display_position().into(),
1566 [
1567 basic_front_resource.display_position()[0]
1568 + basic_front_resource.display_size()[0],
1569 basic_front_resource.display_position()[1]
1570 + basic_front_resource.display_size()[1],
1571 ]
1572 .into(),
1573 ),
1574 Rect::from_min_size(position.into(), size.into()),
1575 ];
1576 let min = resource_rect.min.max(clip_rect.min);
1577 let max = resource_rect.max.min(clip_rect.max);
1578
1579 // 检查是否有交集
1580 if min.x < max.x && min.y < max.y {
1581 [min.into(), max.into()]
1582 } else {
1583 [[0_f32, 0_f32], [0_f32, 0_f32]]
1584 }
1585 } else {
1586 [
1587 basic_front_resource.display_position(),
1588 [
1589 basic_front_resource.display_position()[0]
1590 + basic_front_resource.display_size()[0],
1591 basic_front_resource.display_position()[1]
1592 + basic_front_resource.display_size()[1],
1593 ],
1594 ]
1595 },
1596 display_info.ignore_render_layer,
1597 ));
1598 };
1599 }
1600 Ok(())
1601 }
1602
1603 /// Draw the rendering layer.
1604 ///
1605 /// 绘制渲染层。
1606 ///
1607 /// This method can visually inspect the rendering status of all rendering
1608 /// resources and promptly correct any issues.
1609 ///
1610 /// 此方法可以直观检查所有渲染资源的渲染情况,并及时修正问题。
1611 pub fn display_render_layer(
1612 &self,
1613 ui: &mut Ui,
1614 render_config: &RenderConfig,
1615 ignore_render_config: &RenderConfig,
1616 hover_config: Option<&RenderConfig>,
1617 ) {
1618 for (i, (_, point, ignore_render_layer)) in self.render_layer.iter().enumerate() {
1619 match if *ignore_render_layer {
1620 ignore_render_config
1621 } else {
1622 render_config
1623 } {
1624 RenderConfig::Rect(
1625 corner_radius,
1626 fill_color,
1627 border_color,
1628 border_width,
1629 border_kind,
1630 ) => {
1631 let rect = Rect::from_min_max(point[0].into(), point[1].into());
1632 ui.painter().rect(
1633 rect,
1634 CornerRadius {
1635 nw: corner_radius[0],
1636 ne: corner_radius[1],
1637 sw: corner_radius[2],
1638 se: corner_radius[3],
1639 },
1640 Color32::from_rgba_unmultiplied(
1641 fill_color[0],
1642 fill_color[1],
1643 fill_color[2],
1644 fill_color[3],
1645 ),
1646 Stroke::new(
1647 *border_width,
1648 Color32::from_rgba_unmultiplied(
1649 border_color[0],
1650 border_color[1],
1651 border_color[2],
1652 border_color[3],
1653 ),
1654 ),
1655 match *border_kind {
1656 BorderKind::Inside => StrokeKind::Inside,
1657 BorderKind::Middle => StrokeKind::Middle,
1658 BorderKind::Outside => StrokeKind::Outside,
1659 },
1660 );
1661 }
1662 RenderConfig::Line(width, color) => {
1663 ui.painter().line_segment(
1664 [point[0].into(), point[1].into()],
1665 Stroke::new(
1666 *width,
1667 Color32::from_rgba_unmultiplied(color[0], color[1], color[2], color[3]),
1668 ),
1669 );
1670 }
1671 };
1672 if let Some(hover_config) = hover_config
1673 && let Some(mouse_pos) = ui.input(|i| i.pointer.hover_pos())
1674 && self.resource_get_focus(i, mouse_pos.into(), true, vec![])
1675 {
1676 match hover_config {
1677 RenderConfig::Rect(
1678 corner_radius,
1679 fill_color,
1680 border_color,
1681 border_width,
1682 border_kind,
1683 ) => {
1684 let rect = Rect::from_min_max(point[0].into(), point[1].into());
1685 ui.painter().rect(
1686 rect,
1687 CornerRadius {
1688 nw: corner_radius[0],
1689 ne: corner_radius[1],
1690 sw: corner_radius[2],
1691 se: corner_radius[3],
1692 },
1693 Color32::from_rgba_unmultiplied(
1694 fill_color[0],
1695 fill_color[1],
1696 fill_color[2],
1697 fill_color[3],
1698 ),
1699 Stroke::new(
1700 *border_width,
1701 Color32::from_rgba_unmultiplied(
1702 border_color[0],
1703 border_color[1],
1704 border_color[2],
1705 border_color[3],
1706 ),
1707 ),
1708 match *border_kind {
1709 BorderKind::Inside => StrokeKind::Inside,
1710 BorderKind::Middle => StrokeKind::Middle,
1711 BorderKind::Outside => StrokeKind::Outside,
1712 },
1713 );
1714 }
1715 RenderConfig::Line(width, color) => {
1716 ui.painter().line_segment(
1717 [point[0].into(), point[1].into()],
1718 Stroke::new(
1719 *width,
1720 Color32::from_rgba_unmultiplied(
1721 color[0], color[1], color[2], color[3],
1722 ),
1723 ),
1724 );
1725 }
1726 };
1727 };
1728 }
1729 }
1730
1731 /// Search for resources in the render list by ID.
1732 ///
1733 /// 通过ID在渲染列表中查找资源。
1734 pub fn get_render_layer_resource(&self, id: &RustConstructorId) -> Option<usize> {
1735 self.render_layer.iter().position(|x| &x.0 == id)
1736 }
1737
1738 /// Check whether the resource has obtained the mouse focus.
1739 ///
1740 /// 检查资源是否获取鼠标焦点。
1741 ///
1742 /// Use this method to ensure that mouse operations do not trigger
1743 /// multiple components simultaneously, causing confusion.
1744 ///
1745 /// 使用此方法以保证鼠标操作不会同时触发多个组件产生混乱。
1746 pub fn resource_get_focus(
1747 &self,
1748 index: usize,
1749 mouse_pos: [f32; 2],
1750 need_contains_mouse: bool,
1751 ignore_render_layer: Vec<[usize; 2]>,
1752 ) -> bool {
1753 let mut ignore_list = Vec::new();
1754 for range in ignore_render_layer {
1755 for i in 0..range[1] {
1756 ignore_list.push(range[0] + i);
1757 }
1758 }
1759 for i in index + 1..self.render_layer.len() {
1760 let point = self.render_layer[i].1;
1761 if mouse_pos[0] >= point[0][0]
1762 && mouse_pos[1] >= point[0][1]
1763 && mouse_pos[0] <= point[1][0]
1764 && mouse_pos[1] <= point[1][1]
1765 && !self.render_layer[i].2
1766 && !ignore_list.contains(&i)
1767 {
1768 return false;
1769 };
1770 }
1771 let target_point = self.render_layer[index].1;
1772 !need_contains_mouse
1773 || mouse_pos[0] <= target_point[1][0]
1774 && mouse_pos[0] >= target_point[0][0]
1775 && mouse_pos[1] <= target_point[1][1]
1776 && mouse_pos[1] >= target_point[0][1]
1777 }
1778
1779 /// Mark active resources.
1780 ///
1781 /// 标记活跃资源。
1782 ///
1783 /// This method will be automatically called by the Rust Constructor without
1784 /// the need for manual control.
1785 ///
1786 /// 此方法会被Rust Constructor自动调用,无需手动控制。
1787 pub fn add_active_resource(
1788 &mut self,
1789 id: &RustConstructorId,
1790 ) -> Result<(), RustConstructorError> {
1791 self.active_list.push((
1792 id.clone(),
1793 if let [Some(citer_name), Some(citer_type)] = [
1794 get_tag("citer_name", &self.get_box_resource(id)?.display_tags()),
1795 get_tag("citer_type", &self.get_box_resource(id)?.display_tags()),
1796 ] {
1797 Some(build_id(citer_name.1, citer_type.1))
1798 } else {
1799 None
1800 },
1801 ));
1802 Ok(())
1803 }
1804
1805 /// Adds a new resource to the application with the specified name.
1806 ///
1807 /// 添加一个新资源到应用程序中,并指定名称。
1808 ///
1809 /// This method registers a resource instance with a unique name. If the name is already in use
1810 /// or invalid, an error is returned. For certain resource types like SplitTime, it automatically
1811 /// initializes time values.
1812 ///
1813 /// 此方法使用唯一名称注册资源实例。如果名称已存在或无效,则返回错误。
1814 /// 对于某些资源类型(如 SplitTime),它会自动初始化时间值。
1815 pub fn add_resource<T: RustConstructorResource + 'static>(
1816 &mut self,
1817 name: &str,
1818 mut resource: T,
1819 ) -> Result<(), RustConstructorError> {
1820 let discern_type = &*type_processor(&resource);
1821 if self
1822 .check_resource_exists(&build_id(name, discern_type))
1823 .is_some()
1824 {
1825 error!(
1826 "[ResourceNameRepetition]add_resource: Resource '{name}({discern_type})' has already existed."
1827 );
1828 return Err(RustConstructorError {
1829 error_id: "ResourceNameRepetition".to_string(),
1830 description: format!("Resource '{name}({discern_type})' has already existed."),
1831 });
1832 };
1833 if name.is_empty() {
1834 error!("[ResourceUntitled]add_resource: All resources must have a valid name.");
1835 return Err(RustConstructorError {
1836 error_id: "ResourceUntitled".to_string(),
1837 description: "All resources must have a valid name.".to_string(),
1838 });
1839 };
1840 match discern_type {
1841 "SplitTime" => {
1842 let split_time = downcast_resource_mut::<SplitTime>(&mut resource)?;
1843 split_time.time = [self.timer.now_time, self.timer.total_time];
1844 }
1845 "Background" => {
1846 let background = downcast_resource_mut::<Background>(&mut resource)?;
1847 match &background.background_type {
1848 BackgroundType::CustomRect(config) => {
1849 let mut custom_rect = CustomRect::default().from_config(config);
1850 if background.use_background_tags {
1851 custom_rect.modify_tags(&background.tags, false);
1852 };
1853 self.add_resource(name, custom_rect)
1854 }
1855 BackgroundType::Image(config) => {
1856 let mut image = Image::default().from_config(config);
1857 if background.use_background_tags {
1858 image.modify_tags(&background.tags, false);
1859 };
1860 self.add_resource(name, image)
1861 }
1862 }?;
1863 }
1864 "Switch" => {
1865 let switch = downcast_resource_mut::<Switch>(&mut resource)?;
1866 let count = 1 + switch.enable_animation.iter().filter(|x| **x).count();
1867 if switch.appearance.len() != count * switch.state_amount as usize {
1868 error!(
1869 "[SwitchAppearanceConfigMismatch]add_resource: Expected {} elements, found {}.",
1870 count * switch.state_amount as usize,
1871 switch.appearance.len()
1872 );
1873 return Err(RustConstructorError {
1874 error_id: "SwitchAppearanceConfigMismatch".to_string(),
1875 description: format!(
1876 "Expected {} elements, found {}.",
1877 count * switch.state_amount as usize,
1878 switch.appearance.len()
1879 ),
1880 });
1881 };
1882 if !switch.radio_group.is_empty() {
1883 if !self.rust_constructor_resource.iter().any(|x| {
1884 if let Ok(check_switch) = downcast_resource::<Switch>(&*x.content) {
1885 switch.radio_group == check_switch.radio_group
1886 } else {
1887 false
1888 }
1889 }) {
1890 switch.state = 1;
1891 };
1892 if switch.state_amount != 2 {
1893 error!(
1894 "[SwitchAppearanceConfigMismatch]add_resource: Radio group is only supported for switches with 2 states, found {}.",
1895 switch.state_amount
1896 );
1897 return Err(RustConstructorError {
1898 error_id: "SwitchAppearanceConfigMismatch".to_string(),
1899 description: format!(
1900 "Radio group is only supported for switches with 2 states, found {}.",
1901 switch.state_amount
1902 ),
1903 });
1904 };
1905 };
1906 self.add_resource(
1907 &format!("{name}Background"),
1908 Background::default()
1909 .background_type(&switch.background_type)
1910 .auto_update(true)
1911 .use_background_tags(true)
1912 .tags(
1913 if switch.use_switch_tags {
1914 &switch.tags
1915 } else {
1916 &[]
1917 },
1918 false,
1919 )
1920 .tags(
1921 &[
1922 ["citer_name".to_string(), name.to_string()],
1923 ["citer_type".to_string(), discern_type.to_string()],
1924 ["panel_layout_group".to_string(), name.to_string()],
1925 ],
1926 false,
1927 ),
1928 )?;
1929 self.add_resource(
1930 &format!("{name}Text"),
1931 Text::default()
1932 .from_config(&switch.text_config)
1933 .tags(
1934 if switch.use_switch_tags {
1935 &switch.tags
1936 } else {
1937 &[]
1938 },
1939 false,
1940 )
1941 .tags(
1942 &[
1943 ["citer_name".to_string(), name.to_string()],
1944 ["citer_type".to_string(), discern_type.to_string()],
1945 ["panel_layout_group".to_string(), name.to_string()],
1946 ],
1947 false,
1948 ),
1949 )?;
1950 self.add_resource(
1951 &format!("{name}HintText"),
1952 Text::default()
1953 .from_config(&switch.hint_text_config)
1954 .ignore_render_layer(true)
1955 .hidden(true)
1956 .alpha(0)
1957 .tags(
1958 &[
1959 ["citer_name".to_string(), name.to_string()],
1960 ["citer_type".to_string(), discern_type.to_string()],
1961 ["disable_x_scrolling".to_string(), "".to_string()],
1962 ["disable_y_scrolling".to_string(), "".to_string()],
1963 ],
1964 false,
1965 ),
1966 )?;
1967 self.add_resource(
1968 &format!("{name}StartHoverTime"),
1969 SplitTime::default().tags(
1970 &[
1971 ["citer_name".to_string(), name.to_string()],
1972 ["citer_type".to_string(), discern_type.to_string()],
1973 ],
1974 false,
1975 ),
1976 )?;
1977 self.add_resource(
1978 &format!("{name}HintFadeAnimation"),
1979 SplitTime::default().tags(
1980 &[
1981 ["citer_name".to_string(), name.to_string()],
1982 ["citer_type".to_string(), discern_type.to_string()],
1983 ],
1984 false,
1985 ),
1986 )?;
1987 }
1988 "ResourcePanel" => {
1989 let resource_panel = downcast_resource_mut::<ResourcePanel>(&mut resource)?;
1990 self.add_resource(
1991 &format!("{name}Background"),
1992 Background::default()
1993 .background_type(&resource_panel.background)
1994 .auto_update(true)
1995 .use_background_tags(true)
1996 .tags(
1997 &[
1998 ["citer_name".to_string(), name.to_string()],
1999 ["citer_type".to_string(), discern_type.to_string()],
2000 ],
2001 false,
2002 ),
2003 )?;
2004 if let ScrollBarDisplayMethod::Always(_, _, _) =
2005 &resource_panel.scroll_bar_display_method
2006 {
2007 self.add_resource(
2008 &format!("{name}XScroll"),
2009 Background::default()
2010 .auto_update(true)
2011 .use_background_tags(true)
2012 .tags(
2013 &[
2014 ["citer_name".to_string(), name.to_string()],
2015 ["citer_type".to_string(), discern_type.to_string()],
2016 ],
2017 false,
2018 ),
2019 )?;
2020 self.add_resource(
2021 &format!("{name}YScroll"),
2022 Background::default()
2023 .auto_update(true)
2024 .use_background_tags(true)
2025 .tags(
2026 &[
2027 ["citer_name".to_string(), name.to_string()],
2028 ["citer_type".to_string(), discern_type.to_string()],
2029 ],
2030 false,
2031 ),
2032 )?;
2033 };
2034 if let ScrollBarDisplayMethod::OnlyScroll(_, _, _) =
2035 &resource_panel.scroll_bar_display_method
2036 {
2037 self.add_resource(
2038 &format!("{name}XScroll"),
2039 Background::default()
2040 .auto_update(true)
2041 .use_background_tags(true)
2042 .tags(
2043 &[
2044 ["citer_name".to_string(), name.to_string()],
2045 ["citer_type".to_string(), discern_type.to_string()],
2046 ],
2047 false,
2048 ),
2049 )?;
2050 self.add_resource(
2051 &format!("{name}YScroll"),
2052 Background::default()
2053 .auto_update(true)
2054 .use_background_tags(true)
2055 .tags(
2056 &[
2057 ["citer_name".to_string(), name.to_string()],
2058 ["citer_type".to_string(), discern_type.to_string()],
2059 ],
2060 false,
2061 ),
2062 )?;
2063 self.add_resource(
2064 &format!("{name}ScrollBarXAlpha"),
2065 SplitTime::default().tags(
2066 &[
2067 ["citer_name".to_string(), name.to_string()],
2068 ["citer_type".to_string(), discern_type.to_string()],
2069 ],
2070 false,
2071 ),
2072 )?;
2073 self.add_resource(
2074 &format!("{name}ScrollBarXAlphaStart"),
2075 SplitTime::default().tags(
2076 &[
2077 ["citer_name".to_string(), name.to_string()],
2078 ["citer_type".to_string(), discern_type.to_string()],
2079 ],
2080 false,
2081 ),
2082 )?;
2083 self.add_resource(
2084 &format!("{name}ScrollBarYAlpha"),
2085 SplitTime::default().tags(
2086 &[
2087 ["citer_name".to_string(), name.to_string()],
2088 ["citer_type".to_string(), discern_type.to_string()],
2089 ],
2090 false,
2091 ),
2092 )?;
2093 self.add_resource(
2094 &format!("{name}ScrollBarYAlphaStart"),
2095 SplitTime::default().tags(
2096 &[
2097 ["citer_name".to_string(), name.to_string()],
2098 ["citer_type".to_string(), discern_type.to_string()],
2099 ],
2100 false,
2101 ),
2102 )?;
2103 };
2104 }
2105 _ => {}
2106 };
2107 self.rust_constructor_resource
2108 .push(RustConstructorResourceBox::new(
2109 name,
2110 discern_type,
2111 Box::new(resource),
2112 ));
2113 info!("Added resource: '{name}({discern_type})'");
2114 Ok(())
2115 }
2116
2117 /// Removes a resource from the application. This method is very dangerous! Ensure the resource is no longer in use before deletion.
2118 ///
2119 /// 移除资源。此方法非常危险!务必确保资源一定不再使用后删除。
2120 pub fn drop_resource(&mut self, id: &RustConstructorId) -> Result<(), RustConstructorError> {
2121 if let Some(index) = self.check_resource_exists(id) {
2122 self.rust_constructor_resource.remove(index);
2123 if let Some(index) = self.active_list.iter().position(|x| &x.0 == id) {
2124 self.active_list.remove(index);
2125 };
2126 if let Some(index) = self
2127 .render_layer
2128 .iter()
2129 .position(|x| x.0.name == id.name && x.0.discern_type == id.discern_type)
2130 {
2131 self.render_layer.remove(index);
2132 };
2133 Ok(())
2134 } else {
2135 error!(
2136 "[ResourceNotFound]drop_resource: Resource '{}({})' not found.",
2137 id.name, id.discern_type
2138 );
2139 Err(RustConstructorError {
2140 error_id: "ResourceNotFound".to_string(),
2141 description: format!("Resource '{}({})' not found.", id.name, id.discern_type),
2142 })
2143 }
2144 }
2145
2146 /// Replaces an existing resource with a new one in the application.
2147 ///
2148 /// 用应用程序中的新资源替换现有资源。
2149 pub fn replace_resource<T>(
2150 &mut self,
2151 name: &str,
2152 resource: T,
2153 ) -> Result<(), RustConstructorError>
2154 where
2155 T: RustConstructorResource + 'static,
2156 {
2157 let discern_type = &*type_processor(&resource);
2158 if let Some(index) = self.check_resource_exists(&build_id(name, discern_type)) {
2159 self.rust_constructor_resource[index] =
2160 RustConstructorResourceBox::new(name, discern_type, Box::new(resource));
2161 Ok(())
2162 } else {
2163 error!(
2164 "[ResourceNotFound]replace_resource: Resource '{name}({discern_type})' not found."
2165 );
2166 Err(RustConstructorError {
2167 error_id: "ResourceNotFound".to_string(),
2168 description: format!("Resource '{name}({discern_type})' not found."),
2169 })
2170 }
2171 }
2172
2173 /// Obtain basic front resource from the list.
2174 ///
2175 /// 从列表中获取基本前端资源。
2176 ///
2177 /// If you want to use the basic front resource method, please call this method to retrieve the resource.
2178 ///
2179 /// 如果想要使用基本前端资源的方法,请调用此方法来取出资源。
2180 pub fn get_basic_front_resource(
2181 &self,
2182 id: &RustConstructorId,
2183 ) -> Result<&dyn BasicFrontResource, RustConstructorError> {
2184 match &*id.discern_type {
2185 "Image" => Ok(downcast_resource::<Image>(self.get_box_resource(id)?)?),
2186 "Text" => Ok(downcast_resource::<Text>(self.get_box_resource(id)?)?),
2187 "CustomRect" => Ok(downcast_resource::<CustomRect>(self.get_box_resource(id)?)?),
2188 _ => unreachable!(),
2189 }
2190 }
2191
2192 /// Obtain mutable basic front resource from the list.
2193 ///
2194 /// 从列表中获取可变基本前端资源。
2195 ///
2196 /// If you want to use the basic front resource method and modify the basic front resource, please call
2197 /// this method to retrieve the resource.
2198 ///
2199 /// 如果想要使用基本前端资源的方法并修改基本前端资源,请调用此方法来取出资源。
2200 pub fn get_basic_front_resource_mut(
2201 &mut self,
2202 id: &RustConstructorId,
2203 ) -> Result<&mut dyn BasicFrontResource, RustConstructorError> {
2204 match &*id.discern_type {
2205 "Image" => Ok(downcast_resource_mut::<Image>(
2206 self.get_box_resource_mut(id)?,
2207 )?),
2208 "Text" => Ok(downcast_resource_mut::<Text>(
2209 self.get_box_resource_mut(id)?,
2210 )?),
2211 "CustomRect" => Ok(downcast_resource_mut::<CustomRect>(
2212 self.get_box_resource_mut(id)?,
2213 )?),
2214 _ => unreachable!(),
2215 }
2216 }
2217
2218 /// Obtain the boxed immutable resources from the list.
2219 ///
2220 /// 从列表中获取封装的不可变资源。
2221 ///
2222 /// If you need to use a resource without knowing its type, please use this method to retrieve the resource.
2223 ///
2224 /// 如果需要在不知道类型的情况下使用资源,请使用此方法取出资源。
2225 pub fn get_box_resource(
2226 &self,
2227 id: &RustConstructorId,
2228 ) -> Result<&dyn RustConstructorResource, RustConstructorError> {
2229 if let Some(index) = self.check_resource_exists(id) {
2230 Ok(&*self.rust_constructor_resource[index].content)
2231 } else {
2232 error!(
2233 "[ResourceNotFound]get_box_resource: Resource '{}({})' not found.",
2234 id.name, id.discern_type
2235 );
2236 Err(RustConstructorError {
2237 error_id: "ResourceNotFound".to_string(),
2238 description: format!("Resource '{}({})' not found.", id.name, id.discern_type),
2239 })
2240 }
2241 }
2242
2243 /// Obtain the boxed mutable resources from the list.
2244 ///
2245 /// 从列表中获取封装的可变资源。
2246 ///
2247 /// If you need to use a resource without knowing its type, please use this method to retrieve the resource.
2248 ///
2249 /// 如果需要在不知道类型的情况下使用资源,请使用此方法取出资源。
2250 pub fn get_box_resource_mut(
2251 &mut self,
2252 id: &RustConstructorId,
2253 ) -> Result<&mut dyn RustConstructorResource, RustConstructorError> {
2254 if let Some(index) = self.check_resource_exists(id) {
2255 Ok(&mut *self.rust_constructor_resource[index].content)
2256 } else {
2257 error!(
2258 "[ResourceNotFound]get_box_resource_mut: Resource '{}({})' not found.",
2259 id.name, id.discern_type
2260 );
2261 Err(RustConstructorError {
2262 error_id: "ResourceNotFound".to_string(),
2263 description: format!("Resource '{}({})' not found.", id.name, id.discern_type),
2264 })
2265 }
2266 }
2267
2268 /// Obtain the immutable resources from the list.
2269 ///
2270 /// 从列表中获取不可变资源。
2271 pub fn get_resource<T>(&self, id: &RustConstructorId) -> Result<&T, RustConstructorError>
2272 where
2273 T: RustConstructorResource + 'static,
2274 {
2275 downcast_resource(self.get_box_resource(id)?)
2276 }
2277
2278 /// Obtain the mutable resources from the list.
2279 ///
2280 /// 从列表中获取可变资源。
2281 pub fn get_resource_mut<T>(
2282 &mut self,
2283 id: &RustConstructorId,
2284 ) -> Result<&mut T, RustConstructorError>
2285 where
2286 T: RustConstructorResource + 'static,
2287 {
2288 downcast_resource_mut(self.get_box_resource_mut(id)?)
2289 }
2290
2291 /// Checks if a specific resource exists in the application.
2292 ///
2293 /// 检查应用程序中是否存在特定资源。
2294 pub fn check_resource_exists(&self, id: &RustConstructorId) -> Option<usize> {
2295 self.rust_constructor_resource
2296 .iter()
2297 .position(|x| &x.id == id)
2298 }
2299
2300 /// Quickly adds and uses a resource in one operation.
2301 ///
2302 /// 快速添加并使用资源。
2303 ///
2304 /// This method combines adding a resource to the application and immediately using it.
2305 ///
2306 /// 此方法将资源添加到应用程序并立即使用它。
2307 pub fn quick_place<T: RustConstructorResource + 'static>(
2308 &mut self,
2309 name: &str,
2310 resource: T,
2311 ui: &mut Ui,
2312 ) -> Result<(), RustConstructorError> {
2313 let discern_type = &*type_processor(&resource);
2314 if self
2315 .check_resource_exists(&build_id(name, discern_type))
2316 .is_none()
2317 {
2318 self.add_resource(name, resource)
2319 } else {
2320 self.use_resource(&build_id(name, discern_type), ui)
2321 }
2322 }
2323
2324 /// Use the existing resources.
2325 ///
2326 /// 使用已存在的资源。
2327 ///
2328 /// This method invokes existing resources and performs operations.
2329 ///
2330 /// 此方法调用存在的资源并进行操作。
2331 pub fn use_resource(
2332 &mut self,
2333 id: &RustConstructorId,
2334 ui: &mut Ui,
2335 ) -> Result<(), RustConstructorError> {
2336 if self.check_resource_exists(id).is_some() {
2337 match &*id.discern_type {
2338 "CustomRect" | "Text" | "Image" => {
2339 self.add_active_resource(id)?;
2340 }
2341 "PageData" => {
2342 // 更新帧数
2343 self.update_frame_stats();
2344 // 消费已完成的后台图片加载并创建纹理。
2345 self.process_completed_image_loads(ui);
2346 // 更新渲染队列。
2347 self.update_render_list();
2348 // 绘制渲染队列中的资源。
2349 for i in 0..self.render_list.len() {
2350 self.draw_resource_by_index(ui, i)?;
2351 }
2352 // 更新渲染列表。
2353 self.update_render_layer(ui)?;
2354 // 更新资源活跃状态。
2355 self.active_list.clear();
2356 // 更新字体加载情况。
2357 if !self.loading_fonts.is_empty() {
2358 self.loaded_fonts = self.loading_fonts.clone();
2359 self.loading_fonts.clear();
2360 };
2361 // 更新资源启用状态。
2362 for rcr in &mut self.rust_constructor_resource {
2363 if let Some(display_info) = &mut rcr.content.display_display_info() {
2364 rcr.content.modify_display_info(DisplayInfo {
2365 enable: true,
2366 hidden: display_info.hidden,
2367 ignore_render_layer: display_info.ignore_render_layer,
2368 });
2369 };
2370 }
2371 // 更新计时器
2372 self.update_timer();
2373 let page_data =
2374 self.get_resource::<PageData>(&build_id(&self.current_page, "PageData"))?;
2375 if page_data.forced_update {
2376 ctx_adapter(ui).request_repaint();
2377 };
2378 }
2379 "Background" => {
2380 let background = self.get_resource::<Background>(id)?.clone();
2381 if background.auto_update {
2382 match &background.background_type {
2383 BackgroundType::CustomRect(config) => {
2384 let mut custom_rect = self
2385 .get_resource::<CustomRect>(&build_id(&id.name, "CustomRect"))?
2386 .clone()
2387 .from_config(config);
2388 if background.use_background_tags {
2389 custom_rect.modify_tags(&background.tags, false);
2390 };
2391 self.replace_resource(&id.name, custom_rect)?;
2392 }
2393 BackgroundType::Image(config) => {
2394 let mut image = self
2395 .get_resource::<Image>(&build_id(&id.name, "Image"))?
2396 .clone()
2397 .from_config(config);
2398 if background.use_background_tags {
2399 image.modify_tags(&background.tags, false);
2400 };
2401 self.replace_resource(&id.name, image)?;
2402 }
2403 };
2404 };
2405 match background.background_type {
2406 BackgroundType::CustomRect(_) => {
2407 self.use_resource(&build_id(&id.name, "CustomRect"), ui)
2408 }
2409 BackgroundType::Image(_) => {
2410 self.use_resource(&build_id(&id.name, "Image"), ui)
2411 }
2412 }?;
2413 }
2414 "Switch" => {
2415 let mut switch = self.get_resource::<Switch>(id)?.clone();
2416 let background_name = format!("{}Background", &id.name);
2417 let text_name = format!("{}Text", &id.name);
2418 let hint_name = format!("{}HintText", &id.name);
2419 let start_hover_time = format!("{}StartHoverTime", &id.name);
2420 let hint_fade_animation = format!("{}HintFadeAnimation", &id.name);
2421 let background_id = build_id(&background_name, "Background");
2422 let mut background = self.get_resource::<Background>(&background_id)?.clone();
2423 let background_resource_type = background_type_discern(&switch.background_type);
2424 let background_resource = self.get_basic_front_resource(&build_id(
2425 &background_name,
2426 background_resource_type,
2427 ))?;
2428 let display_info = background_resource.display_display_info();
2429 let mut text = self
2430 .get_resource::<Text>(&build_id(text_name.clone(), "Text"))?
2431 .clone();
2432 let mut hint_text = self
2433 .get_resource::<Text>(&build_id(hint_name.clone(), "Text"))?
2434 .clone();
2435 switch.switched = false;
2436 let animation_count =
2437 1 + switch.enable_animation.iter().filter(|x| **x).count();
2438 let mut clicked = None;
2439 let mut hovered = false;
2440 let mut appearance_count = 0;
2441 // 处理点击事件
2442 if let Some(index) = self.get_render_layer_resource(&build_id(
2443 &background_name,
2444 background_resource_type.to_string(),
2445 )) && switch.enable
2446 && let Some(mouse_pos) = ui.input(|i| i.pointer.hover_pos())
2447 && self.resource_get_focus(index, mouse_pos.into(), true, vec![])
2448 && let Some(display_info) = background_resource.display_display_info()
2449 && !display_info.hidden
2450 {
2451 if !switch.last_frame_hovered {
2452 self.reset_split_time(&start_hover_time)?;
2453 } else if self.timer.total_time - self.get_split_time(&start_hover_time)?[1]
2454 >= 2000
2455 || hint_text.alpha != 0
2456 {
2457 hint_text.alpha = 255;
2458 hint_text
2459 .basic_front_resource_config
2460 .position_size_config
2461 .origin_position = [mouse_pos.x, mouse_pos.y];
2462 };
2463 hint_text
2464 .basic_front_resource_config
2465 .position_size_config
2466 .display_method
2467 .0 = if mouse_pos.x + hint_text.actual_size[0] <= ui.available_width() {
2468 HorizontalAlign::Left
2469 } else {
2470 HorizontalAlign::Right
2471 };
2472 hint_text
2473 .basic_front_resource_config
2474 .position_size_config
2475 .display_method
2476 .1 = if mouse_pos.y + hint_text.actual_size[1] <= ui.available_height()
2477 {
2478 VerticalAlign::Top
2479 } else {
2480 VerticalAlign::Bottom
2481 };
2482 hovered = true;
2483 for (count, click_method) in switch.click_method.iter().enumerate() {
2484 if ui.input(|i| {
2485 switch.last_frame_clicked.is_none()
2486 && i.pointer.button_pressed(click_method.click_method)
2487 || switch.last_frame_clicked.is_some()
2488 && i.pointer.button_down(click_method.click_method)
2489 }) {
2490 clicked = Some(count);
2491 break;
2492 };
2493 }
2494 if let Some(clicked_index) = switch.last_frame_clicked
2495 && clicked.is_none()
2496 {
2497 switch.switched = true;
2498 if switch.click_method[clicked_index].action {
2499 if !switch.radio_group.is_empty() {
2500 self.rust_constructor_resource
2501 .iter_mut()
2502 .filter(|x| &x.id.discern_type == "Switch")
2503 .for_each(|x| {
2504 if let Ok(check_switch) =
2505 downcast_resource_mut::<Switch>(&mut *x.content)
2506 && switch.radio_group == check_switch.radio_group
2507 {
2508 check_switch.state = 0;
2509 };
2510 });
2511 };
2512 if switch.radio_group.is_empty() || switch.state == 0 {
2513 if switch.state < switch.appearance.len() / animation_count - 1
2514 {
2515 switch.state += 1;
2516 } else {
2517 switch.state = 0;
2518 };
2519 }
2520 };
2521 };
2522 appearance_count = if clicked.is_some() {
2523 match switch.enable_animation {
2524 [true, true] => 2,
2525 [true, false] | [false, true] => 1,
2526 [false, false] => 0,
2527 }
2528 } else if switch.enable_animation[0] {
2529 1
2530 } else {
2531 0
2532 };
2533 };
2534
2535 // 若鼠标未悬挂在开关上,逐渐隐藏提示文本
2536 if !hovered {
2537 if switch.last_frame_hovered {
2538 self.reset_split_time(&hint_fade_animation)?;
2539 };
2540 if self.timer.total_time - self.get_split_time(&hint_fade_animation)?[1]
2541 >= self.tick_interval
2542 {
2543 self.reset_split_time(&hint_fade_animation)?;
2544 hint_text.alpha = hint_text.alpha.saturating_sub(10);
2545 };
2546 };
2547
2548 hint_text.display_info.hidden = hint_text.alpha == 0;
2549
2550 // 更新Background样式。
2551 background.background_type = switch.appearance
2552 [switch.state * animation_count + appearance_count]
2553 .background_config
2554 .clone();
2555
2556 background.modify_tags(
2557 if switch.use_switch_tags {
2558 &switch.tags
2559 } else {
2560 &[]
2561 },
2562 false,
2563 );
2564
2565 // 刷新提示Text。
2566 let alpha = hint_text.alpha;
2567 hint_text = hint_text
2568 .from_config(
2569 &switch.appearance[switch.state * animation_count + appearance_count]
2570 .hint_text_config,
2571 )
2572 .ignore_render_layer(true);
2573 hint_text.background_alpha = alpha;
2574 hint_text.alpha = alpha;
2575 hint_text.display_info.hidden = if let Some(display_info) = display_info
2576 && display_info.hidden
2577 {
2578 true
2579 } else {
2580 hint_text.display_info.hidden
2581 };
2582
2583 // 更新Text样式。
2584 text = text
2585 .tags(
2586 if switch.use_switch_tags {
2587 &switch.tags
2588 } else {
2589 &[]
2590 },
2591 false,
2592 )
2593 .from_config(
2594 &switch.appearance[switch.state * animation_count + appearance_count]
2595 .text_config,
2596 );
2597
2598 switch.last_frame_hovered = hovered;
2599 switch.last_frame_clicked = clicked;
2600
2601 self.replace_resource(&text_name, text)?;
2602 self.replace_resource(&hint_name, hint_text)?;
2603 self.replace_resource(&id.name, switch)?;
2604 self.replace_resource(&background_name, background)?;
2605
2606 self.use_resource(&build_id(background_name, "Background"), ui)?;
2607 self.use_resource(&build_id(text_name.clone(), "Text"), ui)?;
2608 if alpha != 0 {
2609 if self
2610 .render_list
2611 .iter()
2612 .any(|x| x.0 == build_id(&hint_name, "Text"))
2613 {
2614 self.try_request_jump_render_list(
2615 RequestMethod::Id(build_id(&hint_name, "Text")),
2616 RequestType::Top,
2617 );
2618 };
2619 self.use_resource(&build_id(&hint_name, "Text"), ui)?;
2620 };
2621 }
2622 "ResourcePanel" => {
2623 let mut resource_panel = self
2624 .get_resource::<ResourcePanel>(&build_id(&id.name, "ResourcePanel"))?
2625 .clone();
2626 let background_name = format!("{}Background", &id.name);
2627 let background = self
2628 .get_resource::<Background>(&build_id(
2629 background_name.clone(),
2630 "Background",
2631 ))?
2632 .clone();
2633 let background_resource: Box<dyn BasicFrontResource> =
2634 match background.background_type.clone() {
2635 BackgroundType::CustomRect(_) => Box::new(
2636 self.get_resource::<CustomRect>(&build_id(
2637 background_name.clone(),
2638 background_type_discern(&background.background_type),
2639 ))?
2640 .clone(),
2641 ),
2642 BackgroundType::Image(_) => Box::new(
2643 self.get_resource::<Image>(&build_id(
2644 background_name.clone(),
2645 background_type_discern(&background.background_type),
2646 ))?
2647 .clone(),
2648 ),
2649 };
2650 let (mut position_size_config, mut position, mut size) = (
2651 background_resource
2652 .display_basic_front_resource_config()
2653 .position_size_config,
2654 background_resource.display_position(),
2655 background_resource.display_size(),
2656 );
2657 let rect = Rect::from_min_size(position.into(), size.into());
2658 resource_panel.scrolled = [false, false];
2659 if resource_panel.resizable.contains(&true)
2660 || resource_panel.movable.contains(&true)
2661 {
2662 position_size_config.x_location_grid = [0_f32, 0_f32];
2663 position_size_config.y_location_grid = [0_f32, 0_f32];
2664 position_size_config.x_size_grid = [0_f32, 0_f32];
2665 position_size_config.y_size_grid = [0_f32, 0_f32];
2666 position_size_config.display_method =
2667 (HorizontalAlign::Left, VerticalAlign::Top);
2668 };
2669 if resource_panel.min_size[0] < 10_f32 {
2670 resource_panel.min_size[0] = 10_f32;
2671 };
2672 if resource_panel.min_size[1] < 10_f32 {
2673 resource_panel.min_size[1] = 10_f32;
2674 };
2675 if position_size_config.origin_size[0] < resource_panel.min_size[0] {
2676 position_size_config.origin_size[0] = resource_panel.min_size[0];
2677 };
2678 if position_size_config.origin_size[1] < resource_panel.min_size[1] {
2679 position_size_config.origin_size[1] = resource_panel.min_size[1];
2680 };
2681 [position, size] = position_size_processor(position_size_config, ui);
2682 let scroll_delta: [f32; 2] = ui.input(|i| i.smooth_scroll_delta).into();
2683 let [x_scroll_delta, y_scroll_delta] =
2684 if scroll_delta[0].abs() >= scroll_delta[1].abs() {
2685 [
2686 if resource_panel.reverse_scroll_direction[0] {
2687 -scroll_delta[0]
2688 } else {
2689 scroll_delta[0]
2690 },
2691 0_f32,
2692 ]
2693 } else {
2694 [
2695 0_f32,
2696 if resource_panel.reverse_scroll_direction[1] {
2697 -scroll_delta[1]
2698 } else {
2699 scroll_delta[1]
2700 },
2701 ]
2702 };
2703 let mut resource_get_focus = [false, false];
2704 if let Some(mouse_pos) = ui.input(|i| i.pointer.hover_pos())
2705 && !resource_panel.hidden
2706 && let Some(index) = self.get_render_layer_resource(&build_id(
2707 background_name.clone(),
2708 background_type_discern(&background.background_type).to_string(),
2709 ))
2710 {
2711 resource_get_focus = [
2712 self.resource_get_focus(index, mouse_pos.into(), false, vec![]),
2713 self.resource_get_focus(
2714 index,
2715 mouse_pos.into(),
2716 true,
2717 vec![[index + 1, resource_panel.resource_storage.len()]],
2718 ),
2719 ];
2720 if resource_get_focus[1] {
2721 if resource_panel.scroll_length_method[0].is_some()
2722 && x_scroll_delta != 0_f32
2723 {
2724 resource_panel.scrolled[0] = true;
2725 resource_panel.scroll_progress[0] = if resource_panel
2726 .scroll_progress[0]
2727 + -x_scroll_delta * resource_panel.scroll_sensitivity
2728 > resource_panel.scroll_length[0]
2729 {
2730 resource_panel.scroll_length[0]
2731 } else if resource_panel.scroll_progress[0]
2732 + -x_scroll_delta * resource_panel.scroll_sensitivity
2733 > 0_f32
2734 {
2735 resource_panel.scroll_progress[0]
2736 + -x_scroll_delta * resource_panel.scroll_sensitivity
2737 } else {
2738 0_f32
2739 };
2740 };
2741 if resource_panel.scroll_length_method[1].is_some()
2742 && y_scroll_delta != 0_f32
2743 {
2744 resource_panel.scrolled[1] = true;
2745 resource_panel.scroll_progress[1] = if resource_panel
2746 .scroll_progress[1]
2747 + -y_scroll_delta * resource_panel.scroll_sensitivity
2748 > resource_panel.scroll_length[1]
2749 {
2750 resource_panel.scroll_length[1]
2751 } else if resource_panel.scroll_progress[1]
2752 + -y_scroll_delta * resource_panel.scroll_sensitivity
2753 > 0_f32
2754 {
2755 resource_panel.scroll_progress[1]
2756 + -y_scroll_delta * resource_panel.scroll_sensitivity
2757 } else {
2758 0_f32
2759 };
2760 };
2761 if resource_panel.raise_on_focus
2762 && ui.input(|i| i.pointer.primary_pressed())
2763 {
2764 self.request_jump_render_list(
2765 RequestMethod::Id(build_id(
2766 background_name.clone(),
2767 background_type_discern(&background.background_type)
2768 .to_string(),
2769 )),
2770 RequestType::Top,
2771 )
2772 .unwrap();
2773 let mut update_list = Vec::new();
2774 for rcr in &self.rust_constructor_resource {
2775 if self
2776 .basic_front_resource_list
2777 .contains(&rcr.id.discern_type)
2778 && let Some(panel_name) =
2779 get_tag("panel_name", &rcr.content.display_tags())
2780 && panel_name.1 == id.name
2781 {
2782 update_list.push(rcr.id.clone());
2783 };
2784 }
2785 for id in update_list {
2786 self.try_request_jump_render_list(
2787 RequestMethod::Id(id),
2788 RequestType::Top,
2789 );
2790 }
2791 if let ScrollBarDisplayMethod::Always(ref background_type, _, _) =
2792 resource_panel.scroll_bar_display_method
2793 {
2794 self.try_request_jump_render_list(
2795 RequestMethod::Id(build_id(
2796 format!("{}XScroll", &id.name),
2797 background_type_discern(background_type).to_string(),
2798 )),
2799 RequestType::Top,
2800 );
2801 self.try_request_jump_render_list(
2802 RequestMethod::Id(build_id(
2803 format!("{}YScroll", &id.name),
2804 background_type_discern(background_type).to_string(),
2805 )),
2806 RequestType::Top,
2807 );
2808 };
2809 if let ScrollBarDisplayMethod::OnlyScroll(
2810 ref background_type,
2811 _,
2812 _,
2813 ) = resource_panel.scroll_bar_display_method
2814 {
2815 self.try_request_jump_render_list(
2816 RequestMethod::Id(build_id(
2817 format!("{}XScroll", &id.name),
2818 background_type_discern(background_type),
2819 )),
2820 RequestType::Top,
2821 );
2822 self.try_request_jump_render_list(
2823 RequestMethod::Id(build_id(
2824 format!("{}YScroll", &id.name),
2825 background_type_discern(background_type),
2826 )),
2827 RequestType::Top,
2828 );
2829 };
2830 };
2831 }
2832 if resource_get_focus[0] {
2833 let top_rect = Rect::from_min_size(
2834 [position[0], position[1]].into(),
2835 [size[0], 3_f32].into(),
2836 );
2837 let bottom_rect = Rect::from_min_size(
2838 [position[0], position[1] + size[1] - 3_f32].into(),
2839 [size[0], 3_f32].into(),
2840 );
2841 let left_rect = Rect::from_min_size(
2842 [position[0], position[1]].into(),
2843 [3_f32, size[1]].into(),
2844 );
2845 let right_rect = Rect::from_min_size(
2846 [position[0] + size[0] - 3_f32, position[1]].into(),
2847 [3_f32, size[1]].into(),
2848 );
2849 match [
2850 top_rect.contains(mouse_pos),
2851 bottom_rect.contains(mouse_pos),
2852 left_rect.contains(mouse_pos),
2853 right_rect.contains(mouse_pos),
2854 ] {
2855 [true, false, false, false] if resource_panel.resizable[0] => {
2856 if resource_panel.last_frame_mouse_status.is_none()
2857 && ui.input(|i| i.pointer.primary_pressed())
2858 {
2859 resource_panel.last_frame_mouse_status = Some((
2860 mouse_pos.into(),
2861 ClickAim::TopResize,
2862 [mouse_pos.x - position[0], mouse_pos.y - position[1]],
2863 ))
2864 };
2865 if size[1] > resource_panel.min_size[1]
2866 && (resource_panel.max_size.is_none()
2867 || size[1] < resource_panel.max_size.unwrap()[1])
2868 {
2869 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeVertical);
2870 } else if resource_panel.max_size.is_some()
2871 && size[1] >= resource_panel.max_size.unwrap()[1]
2872 {
2873 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeSouth);
2874 } else {
2875 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeNorth);
2876 };
2877 }
2878 [false, true, false, false] if resource_panel.resizable[1] => {
2879 if resource_panel.last_frame_mouse_status.is_none()
2880 && ui.input(|i| i.pointer.primary_pressed())
2881 {
2882 resource_panel.last_frame_mouse_status = Some((
2883 mouse_pos.into(),
2884 ClickAim::BottomResize,
2885 [mouse_pos.x - position[0], mouse_pos.y - position[1]],
2886 ))
2887 };
2888 if size[1] > resource_panel.min_size[1]
2889 && (resource_panel.max_size.is_none()
2890 || size[1] < resource_panel.max_size.unwrap()[1])
2891 {
2892 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeVertical);
2893 } else if resource_panel.max_size.is_some()
2894 && size[1] >= resource_panel.max_size.unwrap()[1]
2895 {
2896 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeNorth);
2897 } else {
2898 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeSouth);
2899 };
2900 }
2901 [false, false, true, false] if resource_panel.resizable[2] => {
2902 if resource_panel.last_frame_mouse_status.is_none()
2903 && ui.input(|i| i.pointer.primary_pressed())
2904 {
2905 resource_panel.last_frame_mouse_status = Some((
2906 mouse_pos.into(),
2907 ClickAim::LeftResize,
2908 [mouse_pos.x - position[0], mouse_pos.y - position[1]],
2909 ))
2910 };
2911 if size[0] > resource_panel.min_size[0]
2912 && (resource_panel.max_size.is_none()
2913 || size[0] < resource_panel.max_size.unwrap()[0])
2914 {
2915 ctx_adapter(ui)
2916 .set_cursor_icon(CursorIcon::ResizeHorizontal);
2917 } else if resource_panel.max_size.is_some()
2918 && size[0] >= resource_panel.max_size.unwrap()[0]
2919 {
2920 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeEast);
2921 } else {
2922 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeWest);
2923 };
2924 }
2925 [false, false, false, true] if resource_panel.resizable[3] => {
2926 if resource_panel.last_frame_mouse_status.is_none()
2927 && ui.input(|i| i.pointer.primary_pressed())
2928 {
2929 resource_panel.last_frame_mouse_status = Some((
2930 mouse_pos.into(),
2931 ClickAim::RightResize,
2932 [mouse_pos.x - position[0], mouse_pos.y - position[1]],
2933 ))
2934 };
2935 if size[0] > resource_panel.min_size[0]
2936 && (resource_panel.max_size.is_none()
2937 || size[0] < resource_panel.max_size.unwrap()[0])
2938 {
2939 ctx_adapter(ui)
2940 .set_cursor_icon(CursorIcon::ResizeHorizontal);
2941 } else if resource_panel.max_size.is_some()
2942 && size[0] >= resource_panel.max_size.unwrap()[0]
2943 {
2944 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeWest);
2945 } else {
2946 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeEast);
2947 };
2948 }
2949 [true, false, true, false] => {
2950 match [resource_panel.resizable[0], resource_panel.resizable[2]]
2951 {
2952 [true, true] => {
2953 if resource_panel.last_frame_mouse_status.is_none()
2954 && ui.input(|i| i.pointer.primary_pressed())
2955 {
2956 resource_panel.last_frame_mouse_status = Some((
2957 mouse_pos.into(),
2958 ClickAim::LeftTopResize,
2959 [
2960 mouse_pos.x - position[0],
2961 mouse_pos.y - position[1],
2962 ],
2963 ))
2964 };
2965 if size[0] > resource_panel.min_size[0]
2966 && (resource_panel.max_size.is_none()
2967 || size[0]
2968 < resource_panel.max_size.unwrap()[0])
2969 || size[1] > resource_panel.min_size[1]
2970 && (resource_panel.max_size.is_none()
2971 || size[1]
2972 < resource_panel.max_size.unwrap()[1])
2973 {
2974 ctx_adapter(ui)
2975 .set_cursor_icon(CursorIcon::ResizeNwSe);
2976 } else if resource_panel.max_size.is_some()
2977 && size[0] >= resource_panel.max_size.unwrap()[0]
2978 && size[1] >= resource_panel.max_size.unwrap()[1]
2979 {
2980 ctx_adapter(ui)
2981 .set_cursor_icon(CursorIcon::ResizeSouthEast);
2982 } else {
2983 ctx_adapter(ui)
2984 .set_cursor_icon(CursorIcon::ResizeNorthWest)
2985 };
2986 }
2987 [false, true] => {
2988 if resource_panel.last_frame_mouse_status.is_none()
2989 && ui.input(|i| i.pointer.primary_pressed())
2990 {
2991 resource_panel.last_frame_mouse_status = Some((
2992 mouse_pos.into(),
2993 ClickAim::LeftResize,
2994 [
2995 mouse_pos.x - position[0],
2996 mouse_pos.y - position[1],
2997 ],
2998 ))
2999 };
3000 if size[0] > resource_panel.min_size[0]
3001 && (resource_panel.max_size.is_none()
3002 || size[0]
3003 < resource_panel.max_size.unwrap()[0])
3004 {
3005 ctx_adapter(ui)
3006 .set_cursor_icon(CursorIcon::ResizeHorizontal);
3007 } else if resource_panel.max_size.is_some()
3008 && size[0] >= resource_panel.max_size.unwrap()[0]
3009 {
3010 ctx_adapter(ui)
3011 .set_cursor_icon(CursorIcon::ResizeEast);
3012 } else {
3013 ctx_adapter(ui)
3014 .set_cursor_icon(CursorIcon::ResizeWest);
3015 };
3016 }
3017 [true, false] => {
3018 if resource_panel.last_frame_mouse_status.is_none()
3019 && ui.input(|i| i.pointer.primary_pressed())
3020 {
3021 resource_panel.last_frame_mouse_status = Some((
3022 mouse_pos.into(),
3023 ClickAim::TopResize,
3024 [
3025 mouse_pos.x - position[0],
3026 mouse_pos.y - position[1],
3027 ],
3028 ))
3029 };
3030 if size[1] > resource_panel.min_size[1]
3031 && (resource_panel.max_size.is_none()
3032 || size[1]
3033 < resource_panel.max_size.unwrap()[1])
3034 {
3035 ctx_adapter(ui)
3036 .set_cursor_icon(CursorIcon::ResizeVertical);
3037 } else if resource_panel.max_size.is_some()
3038 && size[1] >= resource_panel.max_size.unwrap()[1]
3039 {
3040 ctx_adapter(ui)
3041 .set_cursor_icon(CursorIcon::ResizeSouth);
3042 } else {
3043 ctx_adapter(ui)
3044 .set_cursor_icon(CursorIcon::ResizeNorth);
3045 };
3046 }
3047 [false, false] => {}
3048 }
3049 }
3050 [false, true, false, true] => {
3051 match [resource_panel.resizable[1], resource_panel.resizable[3]]
3052 {
3053 [true, true] => {
3054 if resource_panel.last_frame_mouse_status.is_none()
3055 && ui.input(|i| i.pointer.primary_pressed())
3056 {
3057 resource_panel.last_frame_mouse_status = Some((
3058 mouse_pos.into(),
3059 ClickAim::RightBottomResize,
3060 [
3061 mouse_pos.x - position[0],
3062 mouse_pos.y - position[1],
3063 ],
3064 ))
3065 };
3066 if size[0] > resource_panel.min_size[0]
3067 && (resource_panel.max_size.is_none()
3068 || size[0]
3069 < resource_panel.max_size.unwrap()[0])
3070 || size[1] > resource_panel.min_size[1]
3071 && (resource_panel.max_size.is_none()
3072 || size[1]
3073 < resource_panel.max_size.unwrap()[1])
3074 {
3075 ctx_adapter(ui)
3076 .set_cursor_icon(CursorIcon::ResizeNwSe);
3077 } else if resource_panel.max_size.is_some()
3078 && size[0] >= resource_panel.max_size.unwrap()[0]
3079 && size[1] >= resource_panel.max_size.unwrap()[1]
3080 {
3081 ctx_adapter(ui)
3082 .set_cursor_icon(CursorIcon::ResizeNorthWest);
3083 } else {
3084 ctx_adapter(ui)
3085 .set_cursor_icon(CursorIcon::ResizeSouthEast)
3086 };
3087 }
3088 [false, true] => {
3089 if resource_panel.last_frame_mouse_status.is_none()
3090 && ui.input(|i| i.pointer.primary_pressed())
3091 {
3092 resource_panel.last_frame_mouse_status = Some((
3093 mouse_pos.into(),
3094 ClickAim::RightResize,
3095 [
3096 mouse_pos.x - position[0],
3097 mouse_pos.y - position[1],
3098 ],
3099 ))
3100 };
3101 if size[0] > resource_panel.min_size[0]
3102 && (resource_panel.max_size.is_none()
3103 || size[0]
3104 < resource_panel.max_size.unwrap()[0])
3105 {
3106 ctx_adapter(ui)
3107 .set_cursor_icon(CursorIcon::ResizeHorizontal);
3108 } else if resource_panel.max_size.is_some()
3109 && size[0] >= resource_panel.max_size.unwrap()[0]
3110 {
3111 ctx_adapter(ui)
3112 .set_cursor_icon(CursorIcon::ResizeWest);
3113 } else {
3114 ctx_adapter(ui)
3115 .set_cursor_icon(CursorIcon::ResizeEast);
3116 };
3117 }
3118 [true, false] => {
3119 if resource_panel.last_frame_mouse_status.is_none()
3120 && ui.input(|i| i.pointer.primary_pressed())
3121 {
3122 resource_panel.last_frame_mouse_status = Some((
3123 mouse_pos.into(),
3124 ClickAim::BottomResize,
3125 [
3126 mouse_pos.x - position[0],
3127 mouse_pos.y - position[1],
3128 ],
3129 ))
3130 };
3131 if size[1] > resource_panel.min_size[1]
3132 && (resource_panel.max_size.is_none()
3133 || size[1]
3134 < resource_panel.max_size.unwrap()[1])
3135 {
3136 ctx_adapter(ui)
3137 .set_cursor_icon(CursorIcon::ResizeVertical);
3138 } else if resource_panel.max_size.is_some()
3139 && size[1] >= resource_panel.max_size.unwrap()[1]
3140 {
3141 ctx_adapter(ui)
3142 .set_cursor_icon(CursorIcon::ResizeNorth);
3143 } else {
3144 ctx_adapter(ui)
3145 .set_cursor_icon(CursorIcon::ResizeSouth);
3146 };
3147 }
3148 [false, false] => {}
3149 }
3150 }
3151 [true, false, false, true] => {
3152 match [resource_panel.resizable[0], resource_panel.resizable[3]]
3153 {
3154 [true, true] => {
3155 if resource_panel.last_frame_mouse_status.is_none()
3156 && ui.input(|i| i.pointer.primary_pressed())
3157 {
3158 resource_panel.last_frame_mouse_status = Some((
3159 mouse_pos.into(),
3160 ClickAim::RightTopResize,
3161 [
3162 mouse_pos.x - position[0],
3163 mouse_pos.y - position[1],
3164 ],
3165 ))
3166 };
3167 if size[0] > resource_panel.min_size[0]
3168 && (resource_panel.max_size.is_none()
3169 || size[0]
3170 < resource_panel.max_size.unwrap()[0])
3171 || size[1] > resource_panel.min_size[1]
3172 && (resource_panel.max_size.is_none()
3173 || size[1]
3174 < resource_panel.max_size.unwrap()[1])
3175 {
3176 ctx_adapter(ui)
3177 .set_cursor_icon(CursorIcon::ResizeNeSw);
3178 } else if resource_panel.max_size.is_some()
3179 && size[0] >= resource_panel.max_size.unwrap()[0]
3180 && size[1] >= resource_panel.max_size.unwrap()[1]
3181 {
3182 ctx_adapter(ui)
3183 .set_cursor_icon(CursorIcon::ResizeSouthWest);
3184 } else {
3185 ctx_adapter(ui)
3186 .set_cursor_icon(CursorIcon::ResizeNorthEast)
3187 };
3188 }
3189 [false, true] => {
3190 if resource_panel.last_frame_mouse_status.is_none()
3191 && ui.input(|i| i.pointer.primary_pressed())
3192 {
3193 resource_panel.last_frame_mouse_status = Some((
3194 mouse_pos.into(),
3195 ClickAim::RightResize,
3196 [
3197 mouse_pos.x - position[0],
3198 mouse_pos.y - position[1],
3199 ],
3200 ))
3201 };
3202 if size[0] > resource_panel.min_size[0]
3203 && (resource_panel.max_size.is_none()
3204 || size[0]
3205 < resource_panel.max_size.unwrap()[0])
3206 {
3207 ctx_adapter(ui)
3208 .set_cursor_icon(CursorIcon::ResizeHorizontal);
3209 } else if resource_panel.max_size.is_some()
3210 && size[0] >= resource_panel.max_size.unwrap()[0]
3211 {
3212 ctx_adapter(ui)
3213 .set_cursor_icon(CursorIcon::ResizeWest);
3214 } else {
3215 ctx_adapter(ui)
3216 .set_cursor_icon(CursorIcon::ResizeEast);
3217 };
3218 }
3219 [true, false] => {
3220 if resource_panel.last_frame_mouse_status.is_none()
3221 && ui.input(|i| i.pointer.primary_pressed())
3222 {
3223 resource_panel.last_frame_mouse_status = Some((
3224 mouse_pos.into(),
3225 ClickAim::TopResize,
3226 [
3227 mouse_pos.x - position[0],
3228 mouse_pos.y - position[1],
3229 ],
3230 ))
3231 };
3232 if size[1] > resource_panel.min_size[1]
3233 && (resource_panel.max_size.is_none()
3234 || size[1]
3235 < resource_panel.max_size.unwrap()[1])
3236 {
3237 ctx_adapter(ui)
3238 .set_cursor_icon(CursorIcon::ResizeVertical);
3239 } else if resource_panel.max_size.is_some()
3240 && size[1] >= resource_panel.max_size.unwrap()[1]
3241 {
3242 ctx_adapter(ui)
3243 .set_cursor_icon(CursorIcon::ResizeSouth);
3244 } else {
3245 ctx_adapter(ui)
3246 .set_cursor_icon(CursorIcon::ResizeNorth);
3247 };
3248 }
3249 [false, false] => {}
3250 }
3251 }
3252 [false, true, true, false] => {
3253 match [resource_panel.resizable[1], resource_panel.resizable[2]]
3254 {
3255 [true, true] => {
3256 if resource_panel.last_frame_mouse_status.is_none()
3257 && ui.input(|i| i.pointer.primary_pressed())
3258 {
3259 resource_panel.last_frame_mouse_status = Some((
3260 mouse_pos.into(),
3261 ClickAim::LeftBottomResize,
3262 [
3263 mouse_pos.x - position[0],
3264 mouse_pos.y - position[1],
3265 ],
3266 ))
3267 };
3268 if size[0] > resource_panel.min_size[0]
3269 && (resource_panel.max_size.is_none()
3270 || size[0]
3271 < resource_panel.max_size.unwrap()[0])
3272 || size[1] > resource_panel.min_size[1]
3273 && (resource_panel.max_size.is_none()
3274 || size[1]
3275 < resource_panel.max_size.unwrap()[1])
3276 {
3277 ctx_adapter(ui)
3278 .set_cursor_icon(CursorIcon::ResizeNeSw);
3279 } else if resource_panel.max_size.is_some()
3280 && size[0] >= resource_panel.max_size.unwrap()[0]
3281 && size[1] >= resource_panel.max_size.unwrap()[1]
3282 {
3283 ctx_adapter(ui)
3284 .set_cursor_icon(CursorIcon::ResizeNorthEast);
3285 } else {
3286 ctx_adapter(ui)
3287 .set_cursor_icon(CursorIcon::ResizeSouthWest)
3288 };
3289 }
3290 [false, true] => {
3291 if resource_panel.last_frame_mouse_status.is_none()
3292 && ui.input(|i| i.pointer.primary_pressed())
3293 {
3294 resource_panel.last_frame_mouse_status = Some((
3295 mouse_pos.into(),
3296 ClickAim::LeftResize,
3297 [
3298 mouse_pos.x - position[0],
3299 mouse_pos.y - position[1],
3300 ],
3301 ))
3302 };
3303 if size[0] > resource_panel.min_size[0]
3304 && (resource_panel.max_size.is_none()
3305 || size[0]
3306 < resource_panel.max_size.unwrap()[0])
3307 {
3308 ctx_adapter(ui)
3309 .set_cursor_icon(CursorIcon::ResizeHorizontal);
3310 } else if resource_panel.max_size.is_some()
3311 && size[0] >= resource_panel.max_size.unwrap()[0]
3312 {
3313 ctx_adapter(ui)
3314 .set_cursor_icon(CursorIcon::ResizeEast);
3315 } else {
3316 ctx_adapter(ui)
3317 .set_cursor_icon(CursorIcon::ResizeWest);
3318 };
3319 }
3320 [true, false] => {
3321 if resource_panel.last_frame_mouse_status.is_none()
3322 && ui.input(|i| i.pointer.primary_pressed())
3323 {
3324 resource_panel.last_frame_mouse_status = Some((
3325 mouse_pos.into(),
3326 ClickAim::BottomResize,
3327 [
3328 mouse_pos.x - position[0],
3329 mouse_pos.y - position[1],
3330 ],
3331 ))
3332 };
3333 if size[1] > resource_panel.min_size[1]
3334 && (resource_panel.max_size.is_none()
3335 || size[1]
3336 < resource_panel.max_size.unwrap()[1])
3337 {
3338 ctx_adapter(ui)
3339 .set_cursor_icon(CursorIcon::ResizeVertical);
3340 } else if resource_panel.max_size.is_some()
3341 && size[1] >= resource_panel.max_size.unwrap()[1]
3342 {
3343 ctx_adapter(ui)
3344 .set_cursor_icon(CursorIcon::ResizeNorth);
3345 } else {
3346 ctx_adapter(ui)
3347 .set_cursor_icon(CursorIcon::ResizeSouth);
3348 };
3349 }
3350 [false, false] => {}
3351 }
3352 }
3353 _ => {}
3354 };
3355 resource_panel.last_frame_mouse_status =
3356 if resource_panel.last_frame_mouse_status.is_none()
3357 && rect.contains(mouse_pos)
3358 && ui.input(|i| i.pointer.primary_pressed())
3359 {
3360 Some((
3361 [mouse_pos.x, mouse_pos.y],
3362 ClickAim::Move,
3363 [mouse_pos.x - position[0], mouse_pos.y - position[1]],
3364 ))
3365 } else if resource_panel.last_frame_mouse_status.is_some()
3366 && !ui.input(|i| i.pointer.primary_released())
3367 {
3368 Some((
3369 [mouse_pos.x, mouse_pos.y],
3370 resource_panel.last_frame_mouse_status.unwrap().1,
3371 resource_panel.last_frame_mouse_status.unwrap().2,
3372 ))
3373 } else {
3374 None
3375 };
3376 } else if ui.input(|i| i.pointer.primary_released()) {
3377 resource_panel.last_frame_mouse_status = None;
3378 };
3379 };
3380 if let Some((mouse_pos, click_aim, offset)) =
3381 resource_panel.last_frame_mouse_status
3382 {
3383 match click_aim {
3384 ClickAim::LeftTopResize => {
3385 if position[0] - mouse_pos[0] + size[0] > resource_panel.min_size[0]
3386 && (resource_panel.max_size.is_none()
3387 || position[0] - mouse_pos[0] + size[0]
3388 < resource_panel.max_size.unwrap()[0])
3389 {
3390 position_size_config.origin_size[0] +=
3391 position[0] - mouse_pos[0];
3392 position_size_config.origin_position[0] = mouse_pos[0];
3393 } else if resource_panel.max_size.is_some()
3394 && position[0] - mouse_pos[0] + size[0]
3395 >= resource_panel.max_size.unwrap()[0]
3396 {
3397 position_size_config.origin_position[0] -=
3398 resource_panel.max_size.unwrap()[0]
3399 - position_size_config.origin_size[0];
3400 position_size_config.origin_size[0] =
3401 resource_panel.max_size.unwrap()[0];
3402 } else {
3403 position_size_config.origin_position[0] += position_size_config
3404 .origin_size[0]
3405 - resource_panel.min_size[0];
3406 position_size_config.origin_size[0] =
3407 resource_panel.min_size[0];
3408 };
3409 if position[1] - mouse_pos[1] + size[1] > resource_panel.min_size[1]
3410 && (resource_panel.max_size.is_none()
3411 || position[1] - mouse_pos[1] + size[1]
3412 < resource_panel.max_size.unwrap()[1])
3413 {
3414 position_size_config.origin_size[1] +=
3415 position[1] - mouse_pos[1];
3416 position_size_config.origin_position[1] = mouse_pos[1];
3417 } else if resource_panel.max_size.is_some()
3418 && position[1] - mouse_pos[1] + size[1]
3419 >= resource_panel.max_size.unwrap()[1]
3420 {
3421 position_size_config.origin_position[1] -=
3422 resource_panel.max_size.unwrap()[1]
3423 - position_size_config.origin_size[1];
3424 position_size_config.origin_size[1] =
3425 resource_panel.max_size.unwrap()[1];
3426 } else {
3427 position_size_config.origin_position[1] += position_size_config
3428 .origin_size[1]
3429 - resource_panel.min_size[1];
3430 position_size_config.origin_size[1] =
3431 resource_panel.min_size[1];
3432 };
3433 if size[0] > resource_panel.min_size[0]
3434 && (resource_panel.max_size.is_none()
3435 || size[0] < resource_panel.max_size.unwrap()[0])
3436 || size[1] > resource_panel.min_size[1]
3437 && (resource_panel.max_size.is_none()
3438 || size[1] < resource_panel.max_size.unwrap()[1])
3439 {
3440 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeNwSe);
3441 } else if resource_panel.max_size.is_some()
3442 && size[0] >= resource_panel.max_size.unwrap()[0]
3443 && size[1] >= resource_panel.max_size.unwrap()[1]
3444 {
3445 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeSouthEast);
3446 } else {
3447 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeNorthWest)
3448 };
3449 }
3450 ClickAim::RightBottomResize => {
3451 if mouse_pos[0] - position[0] > resource_panel.min_size[0]
3452 && (resource_panel.max_size.is_none()
3453 || mouse_pos[0] - position[0]
3454 < resource_panel.max_size.unwrap()[0])
3455 {
3456 position_size_config.origin_size[0] =
3457 mouse_pos[0] - position[0];
3458 } else if resource_panel.max_size.is_some()
3459 && mouse_pos[0] - position[0]
3460 >= resource_panel.max_size.unwrap()[0]
3461 {
3462 position_size_config.origin_size[0] =
3463 resource_panel.max_size.unwrap()[0];
3464 } else {
3465 position_size_config.origin_size[0] =
3466 resource_panel.min_size[0];
3467 };
3468 if mouse_pos[1] - position[1] > resource_panel.min_size[1]
3469 && (resource_panel.max_size.is_none()
3470 || mouse_pos[1] - position[1]
3471 < resource_panel.max_size.unwrap()[1])
3472 {
3473 position_size_config.origin_size[1] =
3474 mouse_pos[1] - position[1];
3475 } else if resource_panel.max_size.is_some()
3476 && mouse_pos[1] - position[1]
3477 >= resource_panel.max_size.unwrap()[1]
3478 {
3479 position_size_config.origin_size[1] =
3480 resource_panel.max_size.unwrap()[1];
3481 } else {
3482 position_size_config.origin_size[1] =
3483 resource_panel.min_size[1];
3484 };
3485 if size[0] > resource_panel.min_size[0]
3486 && (resource_panel.max_size.is_none()
3487 || size[0] < resource_panel.max_size.unwrap()[0])
3488 || size[1] > resource_panel.min_size[1]
3489 && (resource_panel.max_size.is_none()
3490 || size[1] < resource_panel.max_size.unwrap()[1])
3491 {
3492 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeNwSe);
3493 } else if resource_panel.max_size.is_some()
3494 && size[0] >= resource_panel.max_size.unwrap()[0]
3495 && size[1] >= resource_panel.max_size.unwrap()[1]
3496 {
3497 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeNorthWest);
3498 } else {
3499 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeSouthEast)
3500 };
3501 }
3502 ClickAim::RightTopResize => {
3503 if mouse_pos[0] - position[0] > resource_panel.min_size[0]
3504 && (resource_panel.max_size.is_none()
3505 || mouse_pos[0] - position[0]
3506 < resource_panel.max_size.unwrap()[0])
3507 {
3508 position_size_config.origin_size[0] =
3509 mouse_pos[0] - position[0];
3510 } else if resource_panel.max_size.is_some()
3511 && mouse_pos[0] - position[0]
3512 >= resource_panel.max_size.unwrap()[0]
3513 {
3514 position_size_config.origin_size[0] =
3515 resource_panel.max_size.unwrap()[0];
3516 } else {
3517 position_size_config.origin_size[0] =
3518 resource_panel.min_size[0];
3519 };
3520 if position[1] - mouse_pos[1] + size[1] > resource_panel.min_size[1]
3521 && (resource_panel.max_size.is_none()
3522 || position[1] - mouse_pos[1] + size[1]
3523 < resource_panel.max_size.unwrap()[1])
3524 {
3525 position_size_config.origin_size[1] +=
3526 position[1] - mouse_pos[1];
3527 position_size_config.origin_position[1] = mouse_pos[1];
3528 } else if resource_panel.max_size.is_some()
3529 && position[1] - mouse_pos[1] + size[1]
3530 >= resource_panel.max_size.unwrap()[1]
3531 {
3532 position_size_config.origin_position[1] -=
3533 resource_panel.max_size.unwrap()[1]
3534 - position_size_config.origin_size[1];
3535 position_size_config.origin_size[1] =
3536 resource_panel.max_size.unwrap()[1];
3537 } else {
3538 position_size_config.origin_position[1] += position_size_config
3539 .origin_size[1]
3540 - resource_panel.min_size[1];
3541 position_size_config.origin_size[1] =
3542 resource_panel.min_size[1];
3543 };
3544 if size[0] > resource_panel.min_size[0]
3545 && (resource_panel.max_size.is_none()
3546 || size[0] < resource_panel.max_size.unwrap()[0])
3547 || size[1] > resource_panel.min_size[1]
3548 && (resource_panel.max_size.is_none()
3549 || size[1] < resource_panel.max_size.unwrap()[1])
3550 {
3551 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeNeSw);
3552 } else if resource_panel.max_size.is_some()
3553 && size[0] >= resource_panel.max_size.unwrap()[0]
3554 && size[1] >= resource_panel.max_size.unwrap()[1]
3555 {
3556 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeSouthWest);
3557 } else {
3558 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeNorthEast)
3559 };
3560 }
3561 ClickAim::LeftBottomResize => {
3562 if position[0] - mouse_pos[0] + size[0] > resource_panel.min_size[0]
3563 && (resource_panel.max_size.is_none()
3564 || position[0] - mouse_pos[0] + size[0]
3565 < resource_panel.max_size.unwrap()[0])
3566 {
3567 position_size_config.origin_size[0] +=
3568 position[0] - mouse_pos[0];
3569 position_size_config.origin_position[0] = mouse_pos[0];
3570 } else if resource_panel.max_size.is_some()
3571 && position[0] - mouse_pos[0] + size[0]
3572 >= resource_panel.max_size.unwrap()[0]
3573 {
3574 position_size_config.origin_position[0] -=
3575 resource_panel.max_size.unwrap()[0]
3576 - position_size_config.origin_size[0];
3577 position_size_config.origin_size[0] =
3578 resource_panel.max_size.unwrap()[0];
3579 } else {
3580 position_size_config.origin_position[0] += position_size_config
3581 .origin_size[0]
3582 - resource_panel.min_size[0];
3583 position_size_config.origin_size[0] =
3584 resource_panel.min_size[0];
3585 };
3586 if mouse_pos[1] - position[1] > resource_panel.min_size[1]
3587 && (resource_panel.max_size.is_none()
3588 || mouse_pos[1] - position[1]
3589 < resource_panel.max_size.unwrap()[1])
3590 {
3591 position_size_config.origin_size[1] =
3592 mouse_pos[1] - position[1];
3593 } else if resource_panel.max_size.is_some()
3594 && mouse_pos[1] - position[1]
3595 >= resource_panel.max_size.unwrap()[1]
3596 {
3597 position_size_config.origin_size[1] =
3598 resource_panel.max_size.unwrap()[1];
3599 } else {
3600 position_size_config.origin_size[1] =
3601 resource_panel.min_size[1];
3602 };
3603 if size[0] > resource_panel.min_size[0]
3604 && (resource_panel.max_size.is_none()
3605 || size[0] < resource_panel.max_size.unwrap()[0])
3606 || size[1] > resource_panel.min_size[1]
3607 && (resource_panel.max_size.is_none()
3608 || size[1] < resource_panel.max_size.unwrap()[1])
3609 {
3610 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeNeSw);
3611 } else if resource_panel.max_size.is_some()
3612 && size[0] >= resource_panel.max_size.unwrap()[0]
3613 && size[1] >= resource_panel.max_size.unwrap()[1]
3614 {
3615 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeNorthEast);
3616 } else {
3617 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeSouthWest)
3618 };
3619 }
3620 ClickAim::TopResize => {
3621 if position[1] - mouse_pos[1] + size[1] > resource_panel.min_size[1]
3622 && (resource_panel.max_size.is_none()
3623 || position[1] - mouse_pos[1] + size[1]
3624 < resource_panel.max_size.unwrap()[1])
3625 {
3626 position_size_config.origin_size[1] +=
3627 position[1] - mouse_pos[1];
3628 position_size_config.origin_position[1] = mouse_pos[1];
3629 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeVertical);
3630 } else if resource_panel.max_size.is_some()
3631 && position[1] - mouse_pos[1] + size[1]
3632 >= resource_panel.max_size.unwrap()[1]
3633 {
3634 position_size_config.origin_position[1] -=
3635 resource_panel.max_size.unwrap()[1]
3636 - position_size_config.origin_size[1];
3637 position_size_config.origin_size[1] =
3638 resource_panel.max_size.unwrap()[1];
3639 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeSouth);
3640 } else {
3641 position_size_config.origin_position[1] += position_size_config
3642 .origin_size[1]
3643 - resource_panel.min_size[1];
3644 position_size_config.origin_size[1] =
3645 resource_panel.min_size[1];
3646 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeNorth);
3647 };
3648 }
3649 ClickAim::BottomResize => {
3650 if mouse_pos[1] - position[1] > resource_panel.min_size[1]
3651 && (resource_panel.max_size.is_none()
3652 || mouse_pos[1] - position[1]
3653 < resource_panel.max_size.unwrap()[1])
3654 {
3655 position_size_config.origin_size[1] =
3656 mouse_pos[1] - position[1];
3657 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeVertical);
3658 } else if resource_panel.max_size.is_some()
3659 && mouse_pos[1] - position[1]
3660 >= resource_panel.max_size.unwrap()[1]
3661 {
3662 position_size_config.origin_size[1] =
3663 resource_panel.max_size.unwrap()[1];
3664 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeNorth);
3665 } else {
3666 position_size_config.origin_size[1] =
3667 resource_panel.min_size[1];
3668 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeSouth);
3669 };
3670 }
3671 ClickAim::LeftResize => {
3672 if position[0] - mouse_pos[0] + size[0] > resource_panel.min_size[0]
3673 && (resource_panel.max_size.is_none()
3674 || position[0] - mouse_pos[0] + size[0]
3675 < resource_panel.max_size.unwrap()[0])
3676 {
3677 position_size_config.origin_size[0] +=
3678 position[0] - mouse_pos[0];
3679 position_size_config.origin_position[0] = mouse_pos[0];
3680 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeHorizontal);
3681 } else if resource_panel.max_size.is_some()
3682 && position[0] - mouse_pos[0] + size[0]
3683 >= resource_panel.max_size.unwrap()[0]
3684 {
3685 position_size_config.origin_position[0] -=
3686 resource_panel.max_size.unwrap()[0]
3687 - position_size_config.origin_size[0];
3688 position_size_config.origin_size[0] =
3689 resource_panel.max_size.unwrap()[0];
3690 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeEast);
3691 } else {
3692 position_size_config.origin_position[0] += position_size_config
3693 .origin_size[0]
3694 - resource_panel.min_size[0];
3695 position_size_config.origin_size[0] =
3696 resource_panel.min_size[0];
3697 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeWest);
3698 };
3699 }
3700 ClickAim::RightResize => {
3701 if mouse_pos[0] - position[0] > resource_panel.min_size[0]
3702 && (resource_panel.max_size.is_none()
3703 || mouse_pos[0] - position[0]
3704 < resource_panel.max_size.unwrap()[0])
3705 {
3706 position_size_config.origin_size[0] =
3707 mouse_pos[0] - position[0];
3708 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeHorizontal);
3709 } else if resource_panel.max_size.is_some()
3710 && mouse_pos[0] - position[0]
3711 >= resource_panel.max_size.unwrap()[0]
3712 {
3713 position_size_config.origin_size[0] =
3714 resource_panel.max_size.unwrap()[0];
3715 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeWest);
3716 } else {
3717 position_size_config.origin_size[0] =
3718 resource_panel.min_size[0];
3719 ctx_adapter(ui).set_cursor_icon(CursorIcon::ResizeEast);
3720 };
3721 }
3722 ClickAim::Move => {
3723 ctx_adapter(ui).set_cursor_icon(match resource_panel.movable {
3724 [true, true] => CursorIcon::Move,
3725 [true, false] => CursorIcon::ResizeColumn,
3726 [false, true] => CursorIcon::ResizeRow,
3727 [false, false] => CursorIcon::NotAllowed,
3728 });
3729 if resource_panel.movable[0] {
3730 position_size_config.origin_position[0] =
3731 mouse_pos[0] - offset[0];
3732 };
3733 if resource_panel.movable[1] {
3734 position_size_config.origin_position[1] =
3735 mouse_pos[1] - offset[1];
3736 };
3737 }
3738 };
3739 };
3740 [position, size] = position_size_processor(position_size_config, ui);
3741 let background_type = match background.background_type.clone() {
3742 BackgroundType::CustomRect(config) => BackgroundType::CustomRect(
3743 config
3744 .position_size_config(Some(position_size_config))
3745 .hidden(Some(resource_panel.hidden)),
3746 ),
3747 BackgroundType::Image(config) => BackgroundType::Image(
3748 config
3749 .position_size_config(Some(position_size_config))
3750 .hidden(Some(resource_panel.hidden)),
3751 ),
3752 };
3753 self.replace_resource(
3754 &background_name,
3755 background.clone().background_type(&background_type).clone(),
3756 )?;
3757 self.use_resource(&build_id(&background_name, "Background"), ui)?;
3758 type PointList = Vec<([f32; 2], [f32; 2], [bool; 2], Option<String>)>;
3759 let mut resource_point_list: PointList = Vec::new();
3760 let mut use_resource_list = Vec::new();
3761 let mut replace_resource_list = Vec::new();
3762 for rcr in &self.rust_constructor_resource {
3763 if self
3764 .basic_front_resource_list
3765 .contains(&rcr.id.discern_type)
3766 && let Some(panel_name) =
3767 get_tag("panel_name", &rcr.content.display_tags())
3768 && panel_name.1 == id.name
3769 {
3770 if let [Some(citer_name), Some(citer_type)] = [
3771 get_tag("citer_name", &rcr.content.display_tags()),
3772 get_tag("citer_type", &rcr.content.display_tags()),
3773 ] {
3774 if !use_resource_list
3775 .iter()
3776 .any(|x| x == &[citer_name.1.clone(), citer_type.1.clone()])
3777 {
3778 use_resource_list.push([citer_name.1, citer_type.1]);
3779 };
3780 } else if !use_resource_list
3781 .iter()
3782 .any(|x| x == &[rcr.id.name.clone(), rcr.id.discern_type.clone()])
3783 {
3784 use_resource_list
3785 .push([rcr.id.name.clone(), rcr.id.discern_type.clone()]);
3786 };
3787 let mut basic_front_resource: Box<dyn BasicFrontResource> =
3788 match &*rcr.id.discern_type {
3789 "Image" => {
3790 Box::new(downcast_resource::<Image>(&*rcr.content)?.clone())
3791 }
3792 "Text" => {
3793 Box::new(downcast_resource::<Text>(&*rcr.content)?.clone())
3794 }
3795 "CustomRect" => Box::new(
3796 downcast_resource::<CustomRect>(&*rcr.content)?.clone(),
3797 ),
3798 _ => {
3799 unreachable!()
3800 }
3801 };
3802 if !resource_panel
3803 .resource_storage
3804 .iter()
3805 .any(|x| x.id == rcr.id)
3806 {
3807 resource_panel.resource_storage.push(PanelStorage {
3808 id: rcr.id.clone(),
3809 ignore_render_layer: if let Some(display_info) =
3810 basic_front_resource.display_display_info()
3811 {
3812 display_info.ignore_render_layer
3813 } else {
3814 false
3815 },
3816 hidden: if let Some(display_info) =
3817 basic_front_resource.display_display_info()
3818 {
3819 display_info.hidden
3820 } else {
3821 false
3822 },
3823 });
3824 };
3825 let enable_scrolling = [
3826 get_tag("disable_x_scrolling", &rcr.content.display_tags())
3827 .is_none(),
3828 get_tag("disable_y_scrolling", &rcr.content.display_tags())
3829 .is_none(),
3830 ];
3831 let offset = basic_front_resource.display_position_size_config().offset;
3832 basic_front_resource.modify_position_size_config(
3833 basic_front_resource
3834 .display_position_size_config()
3835 .x_location_grid(0_f32, 0_f32)
3836 .y_location_grid(0_f32, 0_f32)
3837 .x_size_grid(0_f32, 0_f32)
3838 .y_size_grid(0_f32, 0_f32)
3839 .offset(
3840 if enable_scrolling[0] {
3841 -resource_panel.scroll_progress[0]
3842 } else {
3843 offset[0]
3844 },
3845 if enable_scrolling[1] {
3846 -resource_panel.scroll_progress[1]
3847 } else {
3848 offset[1]
3849 },
3850 ),
3851 );
3852 let mut layout = resource_panel.overall_layout;
3853 for custom_layout in &resource_panel.custom_layout {
3854 match custom_layout {
3855 CustomPanelLayout::Id(layout_id, panel_layout) => {
3856 if rcr.id.cmp(layout_id) == Ordering::Equal {
3857 layout = *panel_layout;
3858 break;
3859 };
3860 }
3861 CustomPanelLayout::Type(layout_type, panel_layout) => {
3862 if *layout_type == rcr.id.discern_type {
3863 layout = *panel_layout;
3864 }
3865 }
3866 };
3867 }
3868 let panel_layout_group = if let Some(panel_layout_group) =
3869 get_tag("panel_layout_group", &basic_front_resource.display_tags())
3870 {
3871 Some(panel_layout_group.1)
3872 } else {
3873 None
3874 };
3875 match layout.panel_margin {
3876 PanelMargin::Vertical(
3877 [top, bottom, left, right],
3878 move_to_bottom,
3879 ) => {
3880 let mut modify_y = 0_f32;
3881 let [default_x_position, default_y_position] =
3882 match layout.panel_location {
3883 PanelLocation::Absolute([x, y]) => {
3884 [position[0] + x, position[1] + y]
3885 }
3886 PanelLocation::Relative([x, y]) => [
3887 position[0]
3888 + if x[1] != 0_f32 {
3889 size[0] / x[1] * x[0]
3890 } else {
3891 0_f32
3892 },
3893 position[1]
3894 + if y[1] != 0_f32 {
3895 size[1] / y[1] * y[0]
3896 } else {
3897 0_f32
3898 },
3899 ],
3900 };
3901 let default_x_position = match basic_front_resource
3902 .display_position_size_config()
3903 .display_method
3904 .0
3905 {
3906 HorizontalAlign::Left => default_x_position,
3907 HorizontalAlign::Center => {
3908 default_x_position
3909 - basic_front_resource.display_size()[0] / 2.0
3910 }
3911 HorizontalAlign::Right => {
3912 default_x_position
3913 - basic_front_resource.display_size()[0]
3914 }
3915 };
3916 let default_y_position = match basic_front_resource
3917 .display_position_size_config()
3918 .display_method
3919 .1
3920 {
3921 VerticalAlign::Top => default_y_position,
3922 VerticalAlign::Center => {
3923 default_y_position
3924 - basic_front_resource.display_size()[1] / 2.0
3925 }
3926 VerticalAlign::Bottom => {
3927 default_y_position
3928 - basic_front_resource.display_size()[1]
3929 }
3930 };
3931 for point in &resource_point_list {
3932 if let Some(ref point_panel_layout_group) = point.3
3933 && let Some(ref panel_layout_group) = panel_layout_group
3934 && panel_layout_group == point_panel_layout_group
3935 {
3936 continue;
3937 };
3938 if default_x_position - left < point.1[0]
3939 && default_y_position - top + modify_y < point.1[1]
3940 && default_x_position
3941 + basic_front_resource.display_size()[0]
3942 + right
3943 > point.0[0]
3944 && default_y_position
3945 + basic_front_resource.display_size()[1]
3946 + bottom
3947 + modify_y
3948 > point.0[1]
3949 {
3950 if move_to_bottom
3951 && point.1[1] - default_y_position + top > modify_y
3952 {
3953 modify_y = point.1[1] - default_y_position + top;
3954 } else if !move_to_bottom
3955 && point.0[1]
3956 - default_y_position
3957 - basic_front_resource.display_size()[1]
3958 - bottom
3959 < modify_y
3960 {
3961 modify_y = point.0[1]
3962 - default_y_position
3963 - basic_front_resource.display_size()[1]
3964 - bottom;
3965 };
3966 };
3967 }
3968 let real_x_position = match basic_front_resource
3969 .display_position_size_config()
3970 .display_method
3971 .0
3972 {
3973 HorizontalAlign::Left => default_x_position,
3974 HorizontalAlign::Center => {
3975 default_x_position
3976 + basic_front_resource.display_size()[0] / 2.0
3977 }
3978 HorizontalAlign::Right => {
3979 default_x_position
3980 + basic_front_resource.display_size()[0]
3981 }
3982 };
3983 let real_y_position = match basic_front_resource
3984 .display_position_size_config()
3985 .display_method
3986 .1
3987 {
3988 VerticalAlign::Top => default_y_position + modify_y,
3989 VerticalAlign::Center => {
3990 default_y_position
3991 + modify_y
3992 + basic_front_resource.display_size()[1] / 2.0
3993 }
3994 VerticalAlign::Bottom => {
3995 default_y_position
3996 + modify_y
3997 + basic_front_resource.display_size()[1]
3998 }
3999 };
4000 basic_front_resource.modify_position_size_config(
4001 basic_front_resource
4002 .display_position_size_config()
4003 .origin_position(
4004 real_x_position
4005 + left
4006 + resource_panel.inner_margin[2],
4007 real_y_position
4008 + top
4009 + resource_panel.inner_margin[0],
4010 ),
4011 );
4012 replace_resource_list.push((
4013 basic_front_resource.display_position_size_config(),
4014 [rcr.id.name.clone(), rcr.id.discern_type.clone()],
4015 ));
4016 resource_point_list.push((
4017 [real_x_position - left, real_y_position - top],
4018 [
4019 real_x_position
4020 + basic_front_resource.display_size()[0]
4021 + right,
4022 real_y_position
4023 + basic_front_resource.display_size()[1]
4024 + bottom,
4025 ],
4026 enable_scrolling,
4027 panel_layout_group,
4028 ));
4029 }
4030 PanelMargin::Horizontal(
4031 [top, bottom, left, right],
4032 move_to_right,
4033 ) => {
4034 let mut modify_x = 0_f32;
4035 let [default_x_position, default_y_position] =
4036 match layout.panel_location {
4037 PanelLocation::Absolute([x, y]) => {
4038 [position[0] + x, position[1] + y]
4039 }
4040 PanelLocation::Relative([x, y]) => [
4041 position[0]
4042 + if x[1] != 0_f32 {
4043 size[0] / x[1] * x[0]
4044 } else {
4045 0_f32
4046 },
4047 position[1]
4048 + if y[1] != 0_f32 {
4049 size[1] / y[1] * y[0]
4050 } else {
4051 0_f32
4052 },
4053 ],
4054 };
4055 let default_x_position = match basic_front_resource
4056 .display_position_size_config()
4057 .display_method
4058 .0
4059 {
4060 HorizontalAlign::Left => default_x_position,
4061 HorizontalAlign::Center => {
4062 default_x_position
4063 - basic_front_resource.display_size()[0] / 2.0
4064 }
4065 HorizontalAlign::Right => {
4066 default_x_position
4067 - basic_front_resource.display_size()[0]
4068 }
4069 };
4070 let default_y_position = match basic_front_resource
4071 .display_position_size_config()
4072 .display_method
4073 .1
4074 {
4075 VerticalAlign::Top => default_y_position,
4076 VerticalAlign::Center => {
4077 default_y_position
4078 - basic_front_resource.display_size()[1] / 2.0
4079 }
4080 VerticalAlign::Bottom => {
4081 default_y_position
4082 - basic_front_resource.display_size()[1]
4083 }
4084 };
4085 for point in &resource_point_list {
4086 if let Some(ref point_panel_layout_group) = point.3
4087 && let Some(ref panel_layout_group) = panel_layout_group
4088 && panel_layout_group == point_panel_layout_group
4089 {
4090 continue;
4091 };
4092 if default_x_position - left + modify_x < point.1[0]
4093 && default_y_position - top < point.1[1]
4094 && default_x_position
4095 + basic_front_resource.display_size()[0]
4096 + right
4097 + modify_x
4098 > point.0[0]
4099 && default_y_position
4100 + basic_front_resource.display_size()[1]
4101 + bottom
4102 > point.0[1]
4103 {
4104 if move_to_right
4105 && point.1[0] - default_x_position + left > modify_x
4106 {
4107 modify_x = point.1[0] - default_x_position + left;
4108 } else if !move_to_right
4109 && point.0[0]
4110 - default_x_position
4111 - basic_front_resource.display_size()[0]
4112 - right
4113 < modify_x
4114 {
4115 modify_x = point.0[0]
4116 - default_x_position
4117 - basic_front_resource.display_size()[0]
4118 - right;
4119 };
4120 };
4121 }
4122 let real_x_position = match basic_front_resource
4123 .display_position_size_config()
4124 .display_method
4125 .0
4126 {
4127 HorizontalAlign::Left => default_x_position + modify_x,
4128 HorizontalAlign::Center => {
4129 default_x_position
4130 + modify_x
4131 + basic_front_resource.display_size()[0] / 2.0
4132 }
4133 HorizontalAlign::Right => {
4134 default_x_position
4135 + modify_x
4136 + basic_front_resource.display_size()[0]
4137 }
4138 };
4139 let real_y_position = match basic_front_resource
4140 .display_position_size_config()
4141 .display_method
4142 .1
4143 {
4144 VerticalAlign::Top => default_y_position,
4145 VerticalAlign::Center => {
4146 default_y_position
4147 + basic_front_resource.display_size()[1] / 2.0
4148 }
4149 VerticalAlign::Bottom => {
4150 default_y_position
4151 + basic_front_resource.display_size()[1]
4152 }
4153 };
4154 basic_front_resource.modify_position_size_config(
4155 basic_front_resource
4156 .display_position_size_config()
4157 .origin_position(
4158 real_x_position
4159 + left
4160 + resource_panel.inner_margin[2],
4161 real_y_position
4162 + top
4163 + resource_panel.inner_margin[0],
4164 ),
4165 );
4166 replace_resource_list.push((
4167 basic_front_resource.display_position_size_config(),
4168 [rcr.id.name.clone(), rcr.id.discern_type.clone()],
4169 ));
4170 resource_point_list.push((
4171 [real_x_position - left, real_y_position - top],
4172 [
4173 real_x_position
4174 + basic_front_resource.display_size()[0]
4175 + right,
4176 real_y_position
4177 + basic_front_resource.display_size()[1]
4178 + bottom,
4179 ],
4180 enable_scrolling,
4181 panel_layout_group,
4182 ));
4183 }
4184 PanelMargin::None([top, bottom, left, right], influence_layout) => {
4185 let [default_x_position, default_y_position] =
4186 match layout.panel_location {
4187 PanelLocation::Absolute([x, y]) => {
4188 [position[0] + x, position[1] + y]
4189 }
4190 PanelLocation::Relative([x, y]) => [
4191 position[0]
4192 + if x[1] != 0_f32 {
4193 size[0] / x[1] * x[0]
4194 } else {
4195 0_f32
4196 },
4197 position[1]
4198 + if y[1] != 0_f32 {
4199 size[1] / y[1] * y[0]
4200 } else {
4201 0_f32
4202 },
4203 ],
4204 };
4205 basic_front_resource.modify_position_size_config(
4206 basic_front_resource
4207 .display_position_size_config()
4208 .origin_position(
4209 default_x_position
4210 + left
4211 + resource_panel.inner_margin[2],
4212 default_y_position
4213 + top
4214 + resource_panel.inner_margin[0],
4215 ),
4216 );
4217 replace_resource_list.push((
4218 basic_front_resource.display_position_size_config(),
4219 [rcr.id.name.clone(), rcr.id.discern_type.clone()],
4220 ));
4221 if influence_layout {
4222 resource_point_list.push((
4223 [default_x_position - left, default_y_position - top],
4224 [
4225 default_x_position
4226 + basic_front_resource.display_size()[0]
4227 + right,
4228 default_y_position
4229 + basic_front_resource.display_size()[1]
4230 + bottom,
4231 ],
4232 enable_scrolling,
4233 panel_layout_group,
4234 ));
4235 };
4236 }
4237 };
4238 };
4239 }
4240 for (new_position_size_config, [name, discern_type]) in replace_resource_list {
4241 let id = build_id(name, discern_type);
4242 let default_storage = if let Some(resource_storage) =
4243 resource_panel.resource_storage.iter().find(|x| x.id == id)
4244 {
4245 [
4246 true,
4247 resource_storage.ignore_render_layer,
4248 resource_storage.hidden,
4249 ]
4250 } else {
4251 [false, true, true]
4252 };
4253 let basic_front_resource = self.get_basic_front_resource_mut(&id)?;
4254 basic_front_resource.modify_position_size_config(new_position_size_config);
4255 basic_front_resource.modify_clip_rect(Some(
4256 position_size_config
4257 .origin_size(
4258 position_size_config.origin_size[0]
4259 - resource_panel.inner_margin[2]
4260 - resource_panel.inner_margin[3],
4261 position_size_config.origin_size[1]
4262 - resource_panel.inner_margin[0]
4263 - resource_panel.inner_margin[1],
4264 )
4265 .origin_position(
4266 position_size_config.origin_position[0]
4267 + resource_panel.inner_margin[2],
4268 position_size_config.origin_position[1]
4269 + resource_panel.inner_margin[0],
4270 ),
4271 ));
4272 basic_front_resource.modify_display_info({
4273 let mut display_info =
4274 basic_front_resource.display_display_info().unwrap();
4275 display_info.ignore_render_layer =
4276 if resource_panel.last_frame_mouse_status.is_some()
4277 && resource_get_focus[1]
4278 {
4279 true
4280 } else if default_storage[0] {
4281 default_storage[1]
4282 } else {
4283 display_info.ignore_render_layer
4284 };
4285 display_info.hidden = if resource_panel.hidden {
4286 true
4287 } else if default_storage[0] {
4288 default_storage[2]
4289 } else {
4290 display_info.hidden
4291 };
4292 display_info
4293 });
4294 }
4295 for info in use_resource_list {
4296 self.use_resource(&build_id(&info[0], &info[1]), ui)?;
4297 }
4298 let mut resource_length = [None, None];
4299 for point in resource_point_list {
4300 resource_length = [
4301 if resource_length[0].is_none()
4302 || resource_length[0].is_some()
4303 && point.1[0] > resource_length[0].unwrap()
4304 && point.2[0]
4305 {
4306 Some(point.1[0])
4307 } else {
4308 resource_length[0]
4309 },
4310 if resource_length[1].is_none()
4311 || resource_length[1].is_some()
4312 && point.1[1] > resource_length[1].unwrap()
4313 && point.2[1]
4314 {
4315 Some(point.1[1])
4316 } else {
4317 resource_length[1]
4318 },
4319 ]
4320 }
4321 if let Some(horizontal_scroll_length_method) =
4322 resource_panel.scroll_length_method[0]
4323 {
4324 let margin = match resource_panel.overall_layout.panel_margin {
4325 PanelMargin::Horizontal([_, _, left, right], _) => left + right,
4326 PanelMargin::Vertical([_, _, left, right], _) => left + right,
4327 PanelMargin::None([_, _, left, right], _) => left + right,
4328 };
4329 resource_panel.scroll_length[0] = match horizontal_scroll_length_method {
4330 ScrollLengthMethod::Fixed(fixed_length) => fixed_length,
4331 ScrollLengthMethod::AutoFit(expand) => {
4332 if let Some(max) = resource_length[0] {
4333 let width = max - position[0];
4334 if width - size[0]
4335 + expand
4336 + margin
4337 + resource_panel.inner_margin[3]
4338 + resource_panel.inner_margin[2]
4339 > 0_f32
4340 {
4341 width - size[0]
4342 + expand
4343 + margin
4344 + resource_panel.inner_margin[3]
4345 + resource_panel.inner_margin[2]
4346 } else {
4347 0_f32
4348 }
4349 } else {
4350 0_f32
4351 }
4352 }
4353 };
4354 if resource_panel.scroll_progress[0] > resource_panel.scroll_length[0] {
4355 resource_panel.scroll_progress[0] = resource_panel.scroll_length[0];
4356 };
4357 };
4358 if let Some(vertical_scroll_length_method) =
4359 resource_panel.scroll_length_method[1]
4360 {
4361 let margin = match resource_panel.overall_layout.panel_margin {
4362 PanelMargin::Horizontal([top, bottom, _, _], _) => top + bottom,
4363 PanelMargin::Vertical([top, bottom, _, _], _) => top + bottom,
4364 PanelMargin::None([top, bottom, _, _], _) => top + bottom,
4365 };
4366 resource_panel.scroll_length[1] = match vertical_scroll_length_method {
4367 ScrollLengthMethod::Fixed(fixed_length) => fixed_length,
4368 ScrollLengthMethod::AutoFit(expand) => {
4369 if let Some(max) = resource_length[1] {
4370 let height = max - position[1];
4371 if height - size[1]
4372 + expand
4373 + margin
4374 + resource_panel.inner_margin[1]
4375 + resource_panel.inner_margin[0]
4376 > 0_f32
4377 {
4378 height - size[1]
4379 + expand
4380 + margin
4381 + resource_panel.inner_margin[1]
4382 + resource_panel.inner_margin[0]
4383 } else {
4384 0_f32
4385 }
4386 } else {
4387 0_f32
4388 }
4389 }
4390 };
4391 if resource_panel.scroll_progress[1] > resource_panel.scroll_length[1] {
4392 resource_panel.scroll_progress[1] = resource_panel.scroll_length[1];
4393 };
4394 };
4395 match resource_panel.scroll_bar_display_method {
4396 ScrollBarDisplayMethod::Always(ref config, margin, width) => {
4397 let line_length = if resource_panel.scroll_length[1] == 0_f32 {
4398 (size[0] - margin[0] * 2_f32)
4399 * (size[0] / (resource_panel.scroll_length[0] + size[0]))
4400 } else {
4401 (size[0] - width - margin[1] - margin[0] * 2_f32)
4402 * (size[0] / (resource_panel.scroll_length[0] + size[0]))
4403 };
4404 let line_position = if resource_panel.scroll_length[1] == 0_f32 {
4405 position[0]
4406 + margin[0]
4407 + (size[0] - margin[0] * 2_f32 - line_length)
4408 * (resource_panel.scroll_progress[0]
4409 / resource_panel.scroll_length[0])
4410 } else {
4411 position[0]
4412 + margin[0]
4413 + (size[0]
4414 - margin[0] * 2_f32
4415 - width
4416 - margin[1]
4417 - line_length)
4418 * (resource_panel.scroll_progress[0]
4419 / resource_panel.scroll_length[0])
4420 };
4421 self.replace_resource(
4422 &format!("{}XScroll", &id.name),
4423 background.clone().background_type(&match config.clone() {
4424 BackgroundType::CustomRect(config) => {
4425 BackgroundType::CustomRect(
4426 config
4427 .ignore_render_layer(Some(true))
4428 .hidden(Some(resource_panel.hidden))
4429 .position_size_config(Some(
4430 PositionSizeConfig::default()
4431 .display_method(
4432 HorizontalAlign::Left,
4433 VerticalAlign::Bottom,
4434 )
4435 .origin_position(
4436 line_position,
4437 position[1] + size[1] - margin[1],
4438 )
4439 .origin_size(line_length, width),
4440 )),
4441 )
4442 }
4443 BackgroundType::Image(config) => BackgroundType::Image(
4444 config
4445 .ignore_render_layer(Some(true))
4446 .hidden(Some(resource_panel.hidden))
4447 .position_size_config(Some(
4448 PositionSizeConfig::default()
4449 .display_method(
4450 HorizontalAlign::Left,
4451 VerticalAlign::Bottom,
4452 )
4453 .origin_position(
4454 line_position,
4455 position[1] + size[1] - margin[1],
4456 )
4457 .origin_size(line_length, width),
4458 )),
4459 ),
4460 }),
4461 )?;
4462 self.use_resource(
4463 &build_id(format!("{}XScroll", &id.name), "Background"),
4464 ui,
4465 )?;
4466 let line_length = if resource_panel.scroll_length[0] == 0_f32 {
4467 (size[1] - margin[0] * 2_f32)
4468 * (size[1] / (resource_panel.scroll_length[1] + size[1]))
4469 } else {
4470 (size[1] - width - margin[1] - margin[0] * 2_f32)
4471 * (size[1] / (resource_panel.scroll_length[1] + size[1]))
4472 };
4473 let line_position = if resource_panel.scroll_length[0] == 0_f32 {
4474 position[1]
4475 + margin[0]
4476 + (size[1] - margin[0] * 2_f32 - line_length)
4477 * (resource_panel.scroll_progress[1]
4478 / resource_panel.scroll_length[1])
4479 } else {
4480 position[1]
4481 + margin[0]
4482 + (size[1]
4483 - margin[0] * 2_f32
4484 - width
4485 - margin[1]
4486 - line_length)
4487 * (resource_panel.scroll_progress[1]
4488 / resource_panel.scroll_length[1])
4489 };
4490 self.replace_resource(
4491 &format!("{}YScroll", &id.name),
4492 background.background_type(&match config.clone() {
4493 BackgroundType::CustomRect(config) => {
4494 BackgroundType::CustomRect(
4495 config
4496 .ignore_render_layer(Some(true))
4497 .hidden(Some(resource_panel.hidden))
4498 .position_size_config(Some(
4499 PositionSizeConfig::default()
4500 .display_method(
4501 HorizontalAlign::Right,
4502 VerticalAlign::Top,
4503 )
4504 .origin_position(
4505 position[0] + size[0] - margin[1],
4506 line_position,
4507 )
4508 .origin_size(width, line_length),
4509 )),
4510 )
4511 }
4512 BackgroundType::Image(config) => BackgroundType::Image(
4513 config
4514 .ignore_render_layer(Some(true))
4515 .hidden(Some(resource_panel.hidden))
4516 .position_size_config(Some(
4517 PositionSizeConfig::default()
4518 .display_method(
4519 HorizontalAlign::Right,
4520 VerticalAlign::Top,
4521 )
4522 .origin_position(
4523 position[0] + size[0] - margin[1],
4524 line_position,
4525 )
4526 .origin_size(width, line_length),
4527 )),
4528 ),
4529 }),
4530 )?;
4531 self.use_resource(
4532 &build_id(format!("{}YScroll", &id.name), "Background"),
4533 ui,
4534 )?;
4535 }
4536 ScrollBarDisplayMethod::OnlyScroll(ref config, margin, width) => {
4537 resource_panel.scroll_bar_alpha[0] = if resource_panel.scrolled[0] {
4538 self.reset_split_time(&format!(
4539 "{}ScrollBarXAlphaStart",
4540 &id.name
4541 ))?;
4542 255
4543 } else if self.timer.now_time
4544 - self
4545 .get_split_time(&format!("{}ScrollBarXAlphaStart", &id.name))?
4546 [0]
4547 >= 1000
4548 && self.timer.now_time
4549 - self
4550 .get_split_time(&format!("{}ScrollBarXAlpha", &id.name))?[0]
4551 >= self.tick_interval
4552 {
4553 self.reset_split_time(&format!("{}ScrollBarXAlpha", &id.name))?;
4554 resource_panel.scroll_bar_alpha[0].saturating_sub(10)
4555 } else {
4556 resource_panel.scroll_bar_alpha[0]
4557 };
4558 resource_panel.scroll_bar_alpha[1] = if resource_panel.scrolled[1] {
4559 self.reset_split_time(&format!(
4560 "{}ScrollBarYAlphaStart",
4561 &id.name
4562 ))?;
4563 255
4564 } else if self.timer.now_time
4565 - self
4566 .get_split_time(&format!("{}ScrollBarYAlphaStart", &id.name))?
4567 [0]
4568 >= 1000
4569 && self.timer.now_time
4570 - self
4571 .get_split_time(&format!("{}ScrollBarYAlpha", &id.name))?[0]
4572 >= self.tick_interval
4573 {
4574 self.reset_split_time(&format!("{}ScrollBarYAlpha", &id.name))?;
4575 resource_panel.scroll_bar_alpha[1].saturating_sub(10)
4576 } else {
4577 resource_panel.scroll_bar_alpha[1]
4578 };
4579 let line_length = if resource_panel.scroll_length[1] == 0_f32 {
4580 (size[0] - margin[0] * 2_f32)
4581 * (size[0] / (resource_panel.scroll_length[0] + size[0]))
4582 } else {
4583 (size[0] - width - margin[1] - margin[0] * 2_f32)
4584 * (size[0] / (resource_panel.scroll_length[0] + size[0]))
4585 };
4586 let line_position = if resource_panel.scroll_length[1] == 0_f32 {
4587 position[0]
4588 + margin[0]
4589 + (size[0] - margin[0] * 2_f32 - line_length)
4590 * (resource_panel.scroll_progress[0]
4591 / resource_panel.scroll_length[0])
4592 } else {
4593 position[0]
4594 + margin[0]
4595 + (size[0]
4596 - margin[0] * 2_f32
4597 - width
4598 - margin[1]
4599 - line_length)
4600 * (resource_panel.scroll_progress[0]
4601 / resource_panel.scroll_length[0])
4602 };
4603 self.replace_resource(
4604 &format!("{}XScroll", &id.name),
4605 background.clone().background_type(&match config.clone() {
4606 BackgroundType::CustomRect(config) => {
4607 BackgroundType::CustomRect(
4608 config
4609 .ignore_render_layer(Some(true))
4610 .hidden(Some(resource_panel.hidden))
4611 .position_size_config(Some(
4612 PositionSizeConfig::default()
4613 .display_method(
4614 HorizontalAlign::Left,
4615 VerticalAlign::Bottom,
4616 )
4617 .origin_position(
4618 line_position,
4619 position[1] + size[1] - margin[1],
4620 )
4621 .origin_size(line_length, width),
4622 ))
4623 .alpha(Some(resource_panel.scroll_bar_alpha[0]))
4624 .border_alpha(Some(
4625 resource_panel.scroll_bar_alpha[0],
4626 )),
4627 )
4628 }
4629 BackgroundType::Image(config) => BackgroundType::Image(
4630 config
4631 .ignore_render_layer(Some(true))
4632 .hidden(Some(resource_panel.hidden))
4633 .position_size_config(Some(
4634 PositionSizeConfig::default()
4635 .display_method(
4636 HorizontalAlign::Left,
4637 VerticalAlign::Bottom,
4638 )
4639 .origin_position(
4640 line_position,
4641 position[1] + size[1] - margin[1],
4642 )
4643 .origin_size(line_length, width),
4644 ))
4645 .alpha(Some(resource_panel.scroll_bar_alpha[0]))
4646 .background_alpha(Some(
4647 resource_panel.scroll_bar_alpha[0],
4648 ))
4649 .overlay_alpha(Some(
4650 resource_panel.scroll_bar_alpha[0],
4651 )),
4652 ),
4653 }),
4654 )?;
4655 self.use_resource(
4656 &build_id(format!("{}XScroll", &id.name), "Background"),
4657 ui,
4658 )?;
4659 let line_length = if resource_panel.scroll_length[0] == 0_f32 {
4660 (size[1] - margin[0] * 2_f32)
4661 * (size[1] / (resource_panel.scroll_length[1] + size[1]))
4662 } else {
4663 (size[1] - width - margin[1] - margin[0] * 2_f32)
4664 * (size[1] / (resource_panel.scroll_length[1] + size[1]))
4665 };
4666 let line_position = if resource_panel.scroll_length[0] == 0_f32 {
4667 position[1]
4668 + margin[0]
4669 + (size[1] - margin[0] * 2_f32 - line_length)
4670 * (resource_panel.scroll_progress[1]
4671 / resource_panel.scroll_length[1])
4672 } else {
4673 position[1]
4674 + margin[0]
4675 + (size[1]
4676 - margin[0] * 2_f32
4677 - width
4678 - margin[1]
4679 - line_length)
4680 * (resource_panel.scroll_progress[1]
4681 / resource_panel.scroll_length[1])
4682 };
4683 self.replace_resource(
4684 &format!("{}YScroll", &id.name),
4685 background.clone().background_type(&match config.clone() {
4686 BackgroundType::CustomRect(config) => {
4687 BackgroundType::CustomRect(
4688 config
4689 .ignore_render_layer(Some(true))
4690 .hidden(Some(resource_panel.hidden))
4691 .position_size_config(Some(
4692 PositionSizeConfig::default()
4693 .display_method(
4694 HorizontalAlign::Right,
4695 VerticalAlign::Top,
4696 )
4697 .origin_position(
4698 position[0] + size[0] - margin[1],
4699 line_position,
4700 )
4701 .origin_size(width, line_length),
4702 ))
4703 .alpha(Some(resource_panel.scroll_bar_alpha[1]))
4704 .border_alpha(Some(
4705 resource_panel.scroll_bar_alpha[1],
4706 )),
4707 )
4708 }
4709 BackgroundType::Image(config) => BackgroundType::Image(
4710 config
4711 .ignore_render_layer(Some(true))
4712 .hidden(Some(resource_panel.hidden))
4713 .position_size_config(Some(
4714 PositionSizeConfig::default()
4715 .display_method(
4716 HorizontalAlign::Right,
4717 VerticalAlign::Top,
4718 )
4719 .origin_position(
4720 position[0] + size[0] - margin[1],
4721 line_position,
4722 )
4723 .origin_size(width, line_length),
4724 ))
4725 .alpha(Some(resource_panel.scroll_bar_alpha[1]))
4726 .background_alpha(Some(
4727 resource_panel.scroll_bar_alpha[1],
4728 ))
4729 .overlay_alpha(Some(
4730 resource_panel.scroll_bar_alpha[1],
4731 )),
4732 ),
4733 }),
4734 )?;
4735 self.use_resource(
4736 &build_id(format!("{}YScroll", &id.name), "Background"),
4737 ui,
4738 )?;
4739 }
4740 ScrollBarDisplayMethod::Hidden => {}
4741 };
4742 self.replace_resource(&id.name, resource_panel.clone())?;
4743 }
4744 _ => {}
4745 };
4746 Ok(())
4747 } else {
4748 error!(
4749 "[ResourceNotFound]use_resource: Resource '{}({})' not found.",
4750 id.name, id.discern_type
4751 );
4752 Err(RustConstructorError {
4753 error_id: "ResourceNotFound".to_string(),
4754 description: format!("Resource '{}({})' not found.", id.name, id.discern_type),
4755 })
4756 }
4757 }
4758
4759 /// Switches to a different page and resets page-specific state.
4760 ///
4761 /// 切换到不同页面并重置页面特定状态。
4762 pub fn switch_page(&mut self, name: &str) -> Result<(), RustConstructorError> {
4763 let page_data = self.get_resource_mut::<PageData>(&build_id(name, "PageData"))?;
4764 page_data.enter_page_updated = false;
4765 self.timer.start_time = self.timer.total_time;
4766 self.current_page = name.to_string();
4767 self.update_timer();
4768 Ok(())
4769 }
4770
4771 /// Try to register all fonts.
4772 ///
4773 /// 尝试注册所有字体。
4774 ///
4775 /// This method loads and registers all fonts with the egui rendering system for
4776 /// text display.
4777 ///
4778 /// 此方法加载并注册所有字体到egui渲染系统中,用于文本显示。
4779 pub fn try_register_all_fonts(&mut self, ui: &mut Ui, font_info: Vec<[&str; 2]>) {
4780 let mut font_definitions_amount = FontDefinitions::default();
4781 let mut loaded_fonts = Vec::new();
4782 for font_info in font_info {
4783 let mut font = FontDefinitions::default();
4784 if let Ok(font_read_data) = read(font_info[1]) {
4785 let font_data: Arc<Vec<u8>> = Arc::new(font_read_data);
4786 font.font_data.insert(
4787 font_info[0].to_owned(),
4788 Arc::new(FontData::from_owned(
4789 Arc::try_unwrap(font_data).ok().unwrap(),
4790 )),
4791 );
4792 // 将字体添加到字体列表中
4793 font.families
4794 .entry(FontFamily::Proportional)
4795 .or_default()
4796 .insert(0, font_info[0].to_owned());
4797
4798 font.families
4799 .entry(FontFamily::Monospace)
4800 .or_default()
4801 .insert(0, font_info[0].to_owned());
4802 if let Some(font_data) = font.font_data.get(font_info[0]) {
4803 font_definitions_amount
4804 .font_data
4805 .insert(font_info[0].to_string(), Arc::clone(font_data));
4806 font_definitions_amount
4807 .families
4808 .entry(FontFamily::Name(font_info[0].into()))
4809 .or_default()
4810 .push(font_info[0].to_string());
4811 // 将字体添加到字体列表中
4812 font_definitions_amount
4813 .families
4814 .entry(FontFamily::Proportional)
4815 .or_default()
4816 .insert(0, font_info[0].to_owned());
4817
4818 font_definitions_amount
4819 .families
4820 .entry(FontFamily::Monospace)
4821 .or_default()
4822 .insert(0, font_info[0].to_owned());
4823 loaded_fonts.push(font_info);
4824 };
4825 }
4826 }
4827 self.loading_fonts = loaded_fonts
4828 .iter()
4829 .map(|x| [x[0].to_string(), x[1].to_string()])
4830 .collect();
4831 ctx_adapter(ui).set_fonts(font_definitions_amount);
4832 }
4833
4834 /// Registers all fonts.
4835 ///
4836 /// 注册所有字体。
4837 ///
4838 /// This method loads and registers all fonts with the egui rendering system for
4839 /// text display.
4840 ///
4841 /// 此方法加载并注册所有字体到egui渲染系统中,用于文本显示。
4842 pub fn register_all_fonts(
4843 &mut self,
4844 ui: &mut Ui,
4845 font_info: Vec<[&str; 2]>,
4846 ) -> Result<(), RustConstructorError> {
4847 let mut font_definitions_amount = FontDefinitions::default();
4848 let mut loaded_fonts = Vec::new();
4849 for font_info in font_info {
4850 let mut font = FontDefinitions::default();
4851 if let Ok(font_read_data) = read(font_info[1]) {
4852 let font_data: Arc<Vec<u8>> = Arc::new(font_read_data);
4853 font.font_data.insert(
4854 font_info[0].to_owned(),
4855 Arc::new(FontData::from_owned(
4856 Arc::try_unwrap(font_data).ok().unwrap(),
4857 )),
4858 );
4859 // 将字体添加到字体列表中
4860 font.families
4861 .entry(FontFamily::Proportional)
4862 .or_default()
4863 .insert(0, font_info[0].to_owned());
4864
4865 font.families
4866 .entry(FontFamily::Monospace)
4867 .or_default()
4868 .insert(0, font_info[0].to_owned());
4869 if let Some(font_data) = font.font_data.get(font_info[0]) {
4870 font_definitions_amount
4871 .font_data
4872 .insert(font_info[0].to_string(), Arc::clone(font_data));
4873 font_definitions_amount
4874 .families
4875 .entry(FontFamily::Name(font_info[0].into()))
4876 .or_default()
4877 .push(font_info[0].to_string());
4878 // 将字体添加到字体列表中
4879 font_definitions_amount
4880 .families
4881 .entry(FontFamily::Proportional)
4882 .or_default()
4883 .insert(0, font_info[0].to_owned());
4884
4885 font_definitions_amount
4886 .families
4887 .entry(FontFamily::Monospace)
4888 .or_default()
4889 .insert(0, font_info[0].to_owned());
4890 loaded_fonts.push(font_info);
4891 };
4892 } else {
4893 error!(
4894 "[FontLoadFailed]register_all_fonts: Failed to load a font from the path '{}'.",
4895 font_info[1]
4896 );
4897 return Err(RustConstructorError {
4898 error_id: "FontLoadFailed".to_string(),
4899 description: format!("Failed to load a font from the path '{}'.", font_info[1]),
4900 });
4901 }
4902 }
4903 self.loading_fonts = loaded_fonts
4904 .iter()
4905 .map(|x| [x[0].to_string(), x[1].to_string()])
4906 .collect();
4907 ctx_adapter(ui).set_fonts(font_definitions_amount);
4908 Ok(())
4909 }
4910
4911 /// Checks if a page has completed its initial loading phase.
4912 ///
4913 /// 检查页面是否已完成首次加载。
4914 pub fn check_updated(&mut self, name: &str) -> Result<bool, RustConstructorError> {
4915 let page_data = self
4916 .get_resource::<PageData>(&build_id(name, "PageData"))?
4917 .clone();
4918 if !page_data.change_page_updated {
4919 self.new_page_update(name)?;
4920 };
4921 Ok(page_data.change_page_updated)
4922 }
4923
4924 /// Checks if a page has completed its enter transition.
4925 ///
4926 /// 检查页面是否已完成进入过渡。
4927 pub fn check_enter_updated(&mut self, name: &str) -> Result<bool, RustConstructorError> {
4928 let page_data = self.get_resource_mut::<PageData>(&build_id(name, "PageData"))?;
4929 let enter_page_updated = page_data.enter_page_updated;
4930 page_data.enter_page_updated = true;
4931 Ok(enter_page_updated)
4932 }
4933
4934 /// Updates when entering a new page.
4935 ///
4936 /// 进入新页面时的更新。
4937 ///
4938 /// This method is used to ensure the accuracy of the content based on the page, and the Rust Constructor will automatically call this method.
4939 ///
4940 /// 此方法用于确保基于页面的内容的准确性,Rust Constructor会自动调用此方法。
4941 pub fn new_page_update(&mut self, name: &str) -> Result<(), RustConstructorError> {
4942 let page_data = self.get_resource_mut::<PageData>(&build_id(name, "PageData"))?;
4943 page_data.change_page_updated = true;
4944 self.timer.start_time = self.timer.total_time;
4945 self.update_timer();
4946 Ok(())
4947 }
4948
4949 /// Updates frame timing statistics for performance monitoring.
4950 ///
4951 /// 更新帧数统计信息用于性能监控。
4952 ///
4953 /// This method maintains a rolling window of frame times and calculates
4954 /// performance metrics like frame rate.
4955 ///
4956 /// 此方法维护帧时间的滚动窗口并计算帧率等性能指标。
4957 pub fn update_frame_stats(&mut self) {
4958 let current_time = self.timer.total_time;
4959 if let Some(last) = self.last_frame_time {
4960 let delta = current_time - last;
4961 self.frame_times.push(delta);
4962 if self.frame_times.len() > 120 {
4963 self.frame_times.drain(0..120);
4964 }
4965 }
4966 self.last_frame_time = Some(current_time);
4967 }
4968
4969 /// Update the frame rate.
4970 ///
4971 /// 更新帧数。
4972 ///
4973 /// This method is used to obtain the number of program frames and conduct analysis.
4974 ///
4975 /// 此方法用于获取程序帧数并进行分析。
4976 pub fn current_fps(&self) -> f32 {
4977 if self.frame_times.is_empty() {
4978 0.0
4979 } else {
4980 1000_f32
4981 / (self.frame_times.iter().sum::<u128>() as f32 / self.frame_times.len() as f32)
4982 }
4983 }
4984
4985 /// Resets the split time for a specific resource.
4986 ///
4987 /// 重置特定资源的分段计时器。
4988 pub fn reset_split_time(&mut self, name: &str) -> Result<(), RustConstructorError> {
4989 let new_time = [self.timer.now_time, self.timer.total_time];
4990 let split_time = self.get_resource_mut::<SplitTime>(&build_id(name, "SplitTime"))?;
4991 split_time.time = new_time;
4992 Ok(())
4993 }
4994
4995 /// Retrieves the timing information from a split time resource.
4996 ///
4997 /// 获取分段计时器资源的时间信息。
4998 pub fn get_split_time(&self, name: &str) -> Result<[u128; 2], RustConstructorError> {
4999 let split_time = self.get_resource::<SplitTime>(&build_id(name, "SplitTime"))?;
5000 Ok(split_time.time)
5001 }
5002
5003 /// Updates the application timer with current timing information.
5004 ///
5005 /// 更新应用程序计时器的当前时间信息。
5006 ///
5007 /// This method updates both the total runtime and current page runtime.
5008 ///
5009 /// 此方法更新总运行时间和当前页面运行时间。
5010 pub fn update_timer(&mut self) {
5011 let elapsed = self.timer.timer.elapsed();
5012 self.timer.total_time = elapsed.as_millis();
5013 self.timer.now_time = self.timer.total_time - self.timer.start_time
5014 }
5015
5016 /// Modifies the value of a variable resource.
5017 ///
5018 /// 修改变量资源的值。
5019 pub fn modify_variable<T: Debug + Send + Sync + 'static>(
5020 &mut self,
5021 name: &str,
5022 value: Option<T>,
5023 ) -> Result<(), RustConstructorError> {
5024 let variable = self.get_resource_mut::<Variable<T>>(&build_id(name, "Variable"))?;
5025 variable.value = value;
5026 Ok(())
5027 }
5028
5029 /// Take the variable out of the list.
5030 ///
5031 /// 从列表中取出变量。
5032 pub fn get_variable<T: Debug + Clone + Send + Sync + 'static>(
5033 &self,
5034 name: &str,
5035 ) -> Result<Option<T>, RustConstructorError> {
5036 if let Ok(variable) = self.get_resource::<Variable<T>>(&build_id(name, "Variable")) {
5037 Ok(variable.value.clone())
5038 } else if self
5039 .check_resource_exists(&build_id(name, "Variable"))
5040 .is_none()
5041 {
5042 error!("[ResourceNotFound]get_variable: Resource '{name}(Variable<T>)' not found.");
5043 Err(RustConstructorError {
5044 error_id: "ResourceNotFound".to_string(),
5045 description: format!("Resource '{name}(Variable<T>)' not found."),
5046 })
5047 } else {
5048 error!(
5049 "[ResourceGenericMismatch]get_variable: The generic type of the resource '{name}(Variable<T>)' is mismatched."
5050 );
5051 Err(RustConstructorError {
5052 error_id: "ResourceGenericMismatch".to_string(),
5053 description: format!(
5054 "The generic type of the resource '{name}(Variable<T>)' is mismatched."
5055 ),
5056 })
5057 }
5058 }
5059
5060 /// Modify the enable status of the switch.
5061 ///
5062 /// 修改开关的启用状态。
5063 pub fn set_switch_enable(
5064 &mut self,
5065 name: &str,
5066 enable: bool,
5067 ) -> Result<(), RustConstructorError> {
5068 let switch = self.get_resource_mut::<Switch>(&build_id(name, "Switch"))?;
5069 switch.enable = enable;
5070 Ok(())
5071 }
5072
5073 /// Retrieves the current state and interaction data from a switch resource.
5074 ///
5075 /// 获取开关资源的当前状态和交互数据。
5076 pub fn check_switch_data(&self, name: &str) -> Result<SwitchData, RustConstructorError> {
5077 let switch = self.get_resource::<Switch>(&build_id(name, "Switch"))?;
5078 Ok(SwitchData {
5079 switched: switch.switched,
5080 last_frame_clicked: switch.last_frame_clicked,
5081 state: switch.state,
5082 })
5083 }
5084
5085 /// Find out which switch in the radio switch group is activated.
5086 ///
5087 /// 查找单选开关组中哪个开关被激活了。
5088 pub fn check_radio_switch(&self, radio_group: &str) -> String {
5089 let mut activate_switch = String::new();
5090 for rcr in &self.rust_constructor_resource {
5091 if let Ok(switch) = downcast_resource::<Switch>(&*rcr.content)
5092 && switch.radio_group == radio_group
5093 && switch.state == 1
5094 {
5095 activate_switch = rcr.id.name.clone();
5096 break;
5097 };
5098 }
5099 activate_switch
5100 }
5101}