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