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