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