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 seconds.
49 ///
50 /// 资源更新的刷新率(秒)。
51 pub tick_interval: f32,
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<f32>,
67
68 /// Time taken to render the previous frame in seconds.
69 ///
70 /// 渲染上一帧所用的时间(秒)。
71 pub last_frame_time: Option<f32>,
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: 0.05,
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: f32) -> 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 >= 2_f32
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 && resource_panel.raise_on_focus
3111 && ui.input(|i| i.pointer.primary_pressed())
3112 {
3113 self.request_jump_render_list(
3114 RequestMethod::Id(RustConstructorId {
3115 name: format!("{}Background", &id.name),
3116 discern_type: match background.background_type {
3117 BackgroundType::CustomRect(_) => "CustomRect",
3118 BackgroundType::Image(_) => "Image",
3119 }
3120 .to_string(),
3121 }),
3122 RequestType::Top,
3123 )
3124 .unwrap();
3125 let mut update_list = Vec::new();
3126 for rcr in &self.rust_constructor_resource {
3127 if self
3128 .basic_front_resource_list
3129 .contains(&rcr.id.discern_type)
3130 && let Some(panel_name) =
3131 get_tag("panel_name", &rcr.content.display_tags())
3132 && panel_name.1 == id.name
3133 {
3134 update_list.push(rcr.id.clone());
3135 };
3136 }
3137 for id in update_list {
3138 self.try_request_jump_render_list(
3139 RequestMethod::Id(id),
3140 RequestType::Top,
3141 );
3142 }
3143 if let ScrollBarDisplayMethod::Always(ref background_type, _, _) =
3144 resource_panel.scroll_bar_display_method
3145 {
3146 self.try_request_jump_render_list(
3147 RequestMethod::Id(RustConstructorId {
3148 name: format!("{}XScroll", &id.name),
3149 discern_type: match background_type {
3150 BackgroundType::CustomRect(_) => "CustomRect",
3151 BackgroundType::Image(_) => "Image",
3152 }
3153 .to_string(),
3154 }),
3155 RequestType::Top,
3156 );
3157 self.try_request_jump_render_list(
3158 RequestMethod::Id(RustConstructorId {
3159 name: format!("{}YScroll", &id.name),
3160 discern_type: match background_type {
3161 BackgroundType::CustomRect(_) => "CustomRect",
3162 BackgroundType::Image(_) => "Image",
3163 }
3164 .to_string(),
3165 }),
3166 RequestType::Top,
3167 );
3168 };
3169 if let ScrollBarDisplayMethod::OnlyScroll(ref background_type, _, _) =
3170 resource_panel.scroll_bar_display_method
3171 {
3172 self.try_request_jump_render_list(
3173 RequestMethod::Id(RustConstructorId {
3174 name: format!("{}XScroll", &id.name),
3175 discern_type: match background_type {
3176 BackgroundType::CustomRect(_) => "CustomRect",
3177 BackgroundType::Image(_) => "Image",
3178 }
3179 .to_string(),
3180 }),
3181 RequestType::Top,
3182 );
3183 self.try_request_jump_render_list(
3184 RequestMethod::Id(RustConstructorId {
3185 name: format!("{}YScroll", &id.name),
3186 discern_type: match background_type {
3187 BackgroundType::CustomRect(_) => "CustomRect",
3188 BackgroundType::Image(_) => "Image",
3189 }
3190 .to_string(),
3191 }),
3192 RequestType::Top,
3193 );
3194 };
3195 };
3196 if resource_get_focus[0] {
3197 let top_rect = Rect::from_min_size(
3198 [position[0], position[1]].into(),
3199 [size[0], 3_f32].into(),
3200 );
3201 let bottom_rect = Rect::from_min_size(
3202 [position[0], position[1] + size[1] - 3_f32].into(),
3203 [size[0], 3_f32].into(),
3204 );
3205 let left_rect = Rect::from_min_size(
3206 [position[0], position[1]].into(),
3207 [3_f32, size[1]].into(),
3208 );
3209 let right_rect = Rect::from_min_size(
3210 [position[0] + size[0] - 3_f32, position[1]].into(),
3211 [3_f32, size[1]].into(),
3212 );
3213 match [
3214 top_rect.contains(mouse_pos),
3215 bottom_rect.contains(mouse_pos),
3216 left_rect.contains(mouse_pos),
3217 right_rect.contains(mouse_pos),
3218 ] {
3219 [true, false, false, false] => {
3220 if resource_panel.resizable[0] {
3221 if resource_panel.last_frame_mouse_status.is_none()
3222 && ui.input(|i| i.pointer.primary_pressed())
3223 {
3224 resource_panel.last_frame_mouse_status = Some((
3225 mouse_pos.into(),
3226 ClickAim::TopResize,
3227 [
3228 mouse_pos.x - position[0],
3229 mouse_pos.y - position[1],
3230 ],
3231 ))
3232 };
3233 if size[1] > resource_panel.min_size[1]
3234 && (resource_panel.max_size.is_none()
3235 || size[1] < resource_panel.max_size.unwrap()[1])
3236 {
3237 ctx.set_cursor_icon(CursorIcon::ResizeVertical);
3238 } else if resource_panel.max_size.is_some()
3239 && size[1] >= resource_panel.max_size.unwrap()[1]
3240 {
3241 ctx.set_cursor_icon(CursorIcon::ResizeSouth);
3242 } else {
3243 ctx.set_cursor_icon(CursorIcon::ResizeNorth);
3244 };
3245 };
3246 }
3247 [false, true, false, false] => {
3248 if resource_panel.resizable[1] {
3249 if resource_panel.last_frame_mouse_status.is_none()
3250 && ui.input(|i| i.pointer.primary_pressed())
3251 {
3252 resource_panel.last_frame_mouse_status = Some((
3253 mouse_pos.into(),
3254 ClickAim::BottomResize,
3255 [
3256 mouse_pos.x - position[0],
3257 mouse_pos.y - position[1],
3258 ],
3259 ))
3260 };
3261 if size[1] > resource_panel.min_size[1]
3262 && (resource_panel.max_size.is_none()
3263 || size[1] < resource_panel.max_size.unwrap()[1])
3264 {
3265 ctx.set_cursor_icon(CursorIcon::ResizeVertical);
3266 } else if resource_panel.max_size.is_some()
3267 && size[1] >= resource_panel.max_size.unwrap()[1]
3268 {
3269 ctx.set_cursor_icon(CursorIcon::ResizeNorth);
3270 } else {
3271 ctx.set_cursor_icon(CursorIcon::ResizeSouth);
3272 };
3273 };
3274 }
3275 [false, false, true, false] => {
3276 if resource_panel.resizable[2] {
3277 if resource_panel.last_frame_mouse_status.is_none()
3278 && ui.input(|i| i.pointer.primary_pressed())
3279 {
3280 resource_panel.last_frame_mouse_status = Some((
3281 mouse_pos.into(),
3282 ClickAim::LeftResize,
3283 [
3284 mouse_pos.x - position[0],
3285 mouse_pos.y - position[1],
3286 ],
3287 ))
3288 };
3289 if size[0] > resource_panel.min_size[0]
3290 && (resource_panel.max_size.is_none()
3291 || size[0] < resource_panel.max_size.unwrap()[0])
3292 {
3293 ctx.set_cursor_icon(CursorIcon::ResizeHorizontal);
3294 } else if resource_panel.max_size.is_some()
3295 && size[0] >= resource_panel.max_size.unwrap()[0]
3296 {
3297 ctx.set_cursor_icon(CursorIcon::ResizeEast);
3298 } else {
3299 ctx.set_cursor_icon(CursorIcon::ResizeWest);
3300 };
3301 };
3302 }
3303 [false, false, false, true] => {
3304 if resource_panel.resizable[3] {
3305 if resource_panel.last_frame_mouse_status.is_none()
3306 && ui.input(|i| i.pointer.primary_pressed())
3307 {
3308 resource_panel.last_frame_mouse_status = Some((
3309 mouse_pos.into(),
3310 ClickAim::RightResize,
3311 [
3312 mouse_pos.x - position[0],
3313 mouse_pos.y - position[1],
3314 ],
3315 ))
3316 };
3317 if size[0] > resource_panel.min_size[0]
3318 && (resource_panel.max_size.is_none()
3319 || size[0] < resource_panel.max_size.unwrap()[0])
3320 {
3321 ctx.set_cursor_icon(CursorIcon::ResizeHorizontal);
3322 } else if resource_panel.max_size.is_some()
3323 && size[0] >= resource_panel.max_size.unwrap()[0]
3324 {
3325 ctx.set_cursor_icon(CursorIcon::ResizeWest);
3326 } else {
3327 ctx.set_cursor_icon(CursorIcon::ResizeEast);
3328 };
3329 };
3330 }
3331 [true, false, true, false] => {
3332 match [resource_panel.resizable[0], resource_panel.resizable[2]]
3333 {
3334 [true, true] => {
3335 if resource_panel.last_frame_mouse_status.is_none()
3336 && ui.input(|i| i.pointer.primary_pressed())
3337 {
3338 resource_panel.last_frame_mouse_status = Some((
3339 mouse_pos.into(),
3340 ClickAim::LeftTopResize,
3341 [
3342 mouse_pos.x - position[0],
3343 mouse_pos.y - position[1],
3344 ],
3345 ))
3346 };
3347 if size[0] > resource_panel.min_size[0]
3348 && (resource_panel.max_size.is_none()
3349 || size[0]
3350 < resource_panel.max_size.unwrap()[0])
3351 || size[1] > resource_panel.min_size[1]
3352 && (resource_panel.max_size.is_none()
3353 || size[1]
3354 < resource_panel.max_size.unwrap()[1])
3355 {
3356 ctx.set_cursor_icon(CursorIcon::ResizeNwSe);
3357 } else if resource_panel.max_size.is_some()
3358 && size[0] >= resource_panel.max_size.unwrap()[0]
3359 && size[1] >= resource_panel.max_size.unwrap()[1]
3360 {
3361 ctx.set_cursor_icon(CursorIcon::ResizeSouthEast);
3362 } else {
3363 ctx.set_cursor_icon(CursorIcon::ResizeNorthWest)
3364 };
3365 }
3366 [false, true] => {
3367 if resource_panel.last_frame_mouse_status.is_none()
3368 && ui.input(|i| i.pointer.primary_pressed())
3369 {
3370 resource_panel.last_frame_mouse_status = Some((
3371 mouse_pos.into(),
3372 ClickAim::LeftResize,
3373 [
3374 mouse_pos.x - position[0],
3375 mouse_pos.y - position[1],
3376 ],
3377 ))
3378 };
3379 if size[0] > resource_panel.min_size[0]
3380 && (resource_panel.max_size.is_none()
3381 || size[0]
3382 < resource_panel.max_size.unwrap()[0])
3383 {
3384 ctx.set_cursor_icon(CursorIcon::ResizeHorizontal);
3385 } else if resource_panel.max_size.is_some()
3386 && size[0] >= resource_panel.max_size.unwrap()[0]
3387 {
3388 ctx.set_cursor_icon(CursorIcon::ResizeEast);
3389 } else {
3390 ctx.set_cursor_icon(CursorIcon::ResizeWest);
3391 };
3392 }
3393 [true, false] => {
3394 if resource_panel.last_frame_mouse_status.is_none()
3395 && ui.input(|i| i.pointer.primary_pressed())
3396 {
3397 resource_panel.last_frame_mouse_status = Some((
3398 mouse_pos.into(),
3399 ClickAim::TopResize,
3400 [
3401 mouse_pos.x - position[0],
3402 mouse_pos.y - position[1],
3403 ],
3404 ))
3405 };
3406 if size[1] > resource_panel.min_size[1]
3407 && (resource_panel.max_size.is_none()
3408 || size[1]
3409 < resource_panel.max_size.unwrap()[1])
3410 {
3411 ctx.set_cursor_icon(CursorIcon::ResizeVertical);
3412 } else if resource_panel.max_size.is_some()
3413 && size[1] >= resource_panel.max_size.unwrap()[1]
3414 {
3415 ctx.set_cursor_icon(CursorIcon::ResizeSouth);
3416 } else {
3417 ctx.set_cursor_icon(CursorIcon::ResizeNorth);
3418 };
3419 }
3420 [false, false] => {}
3421 }
3422 }
3423 [false, true, false, true] => {
3424 match [resource_panel.resizable[1], resource_panel.resizable[3]]
3425 {
3426 [true, true] => {
3427 if resource_panel.last_frame_mouse_status.is_none()
3428 && ui.input(|i| i.pointer.primary_pressed())
3429 {
3430 resource_panel.last_frame_mouse_status = Some((
3431 mouse_pos.into(),
3432 ClickAim::RightBottomResize,
3433 [
3434 mouse_pos.x - position[0],
3435 mouse_pos.y - position[1],
3436 ],
3437 ))
3438 };
3439 if size[0] > resource_panel.min_size[0]
3440 && (resource_panel.max_size.is_none()
3441 || size[0]
3442 < resource_panel.max_size.unwrap()[0])
3443 || size[1] > resource_panel.min_size[1]
3444 && (resource_panel.max_size.is_none()
3445 || size[1]
3446 < resource_panel.max_size.unwrap()[1])
3447 {
3448 ctx.set_cursor_icon(CursorIcon::ResizeNwSe);
3449 } else if resource_panel.max_size.is_some()
3450 && size[0] >= resource_panel.max_size.unwrap()[0]
3451 && size[1] >= resource_panel.max_size.unwrap()[1]
3452 {
3453 ctx.set_cursor_icon(CursorIcon::ResizeNorthWest);
3454 } else {
3455 ctx.set_cursor_icon(CursorIcon::ResizeSouthEast)
3456 };
3457 }
3458 [false, true] => {
3459 if resource_panel.last_frame_mouse_status.is_none()
3460 && ui.input(|i| i.pointer.primary_pressed())
3461 {
3462 resource_panel.last_frame_mouse_status = Some((
3463 mouse_pos.into(),
3464 ClickAim::RightResize,
3465 [
3466 mouse_pos.x - position[0],
3467 mouse_pos.y - position[1],
3468 ],
3469 ))
3470 };
3471 if size[0] > resource_panel.min_size[0]
3472 && (resource_panel.max_size.is_none()
3473 || size[0]
3474 < resource_panel.max_size.unwrap()[0])
3475 {
3476 ctx.set_cursor_icon(CursorIcon::ResizeHorizontal);
3477 } else if resource_panel.max_size.is_some()
3478 && size[0] >= resource_panel.max_size.unwrap()[0]
3479 {
3480 ctx.set_cursor_icon(CursorIcon::ResizeWest);
3481 } else {
3482 ctx.set_cursor_icon(CursorIcon::ResizeEast);
3483 };
3484 }
3485 [true, false] => {
3486 if resource_panel.last_frame_mouse_status.is_none()
3487 && ui.input(|i| i.pointer.primary_pressed())
3488 {
3489 resource_panel.last_frame_mouse_status = Some((
3490 mouse_pos.into(),
3491 ClickAim::BottomResize,
3492 [
3493 mouse_pos.x - position[0],
3494 mouse_pos.y - position[1],
3495 ],
3496 ))
3497 };
3498 if size[1] > resource_panel.min_size[1]
3499 && (resource_panel.max_size.is_none()
3500 || size[1]
3501 < resource_panel.max_size.unwrap()[1])
3502 {
3503 ctx.set_cursor_icon(CursorIcon::ResizeVertical);
3504 } else if resource_panel.max_size.is_some()
3505 && size[1] >= resource_panel.max_size.unwrap()[1]
3506 {
3507 ctx.set_cursor_icon(CursorIcon::ResizeNorth);
3508 } else {
3509 ctx.set_cursor_icon(CursorIcon::ResizeSouth);
3510 };
3511 }
3512 [false, false] => {}
3513 }
3514 }
3515 [true, false, false, true] => {
3516 match [resource_panel.resizable[0], resource_panel.resizable[3]]
3517 {
3518 [true, true] => {
3519 if resource_panel.last_frame_mouse_status.is_none()
3520 && ui.input(|i| i.pointer.primary_pressed())
3521 {
3522 resource_panel.last_frame_mouse_status = Some((
3523 mouse_pos.into(),
3524 ClickAim::RightTopResize,
3525 [
3526 mouse_pos.x - position[0],
3527 mouse_pos.y - position[1],
3528 ],
3529 ))
3530 };
3531 if size[0] > resource_panel.min_size[0]
3532 && (resource_panel.max_size.is_none()
3533 || size[0]
3534 < resource_panel.max_size.unwrap()[0])
3535 || size[1] > resource_panel.min_size[1]
3536 && (resource_panel.max_size.is_none()
3537 || size[1]
3538 < resource_panel.max_size.unwrap()[1])
3539 {
3540 ctx.set_cursor_icon(CursorIcon::ResizeNeSw);
3541 } else if resource_panel.max_size.is_some()
3542 && size[0] >= resource_panel.max_size.unwrap()[0]
3543 && size[1] >= resource_panel.max_size.unwrap()[1]
3544 {
3545 ctx.set_cursor_icon(CursorIcon::ResizeSouthWest);
3546 } else {
3547 ctx.set_cursor_icon(CursorIcon::ResizeNorthEast)
3548 };
3549 }
3550 [false, true] => {
3551 if resource_panel.last_frame_mouse_status.is_none()
3552 && ui.input(|i| i.pointer.primary_pressed())
3553 {
3554 resource_panel.last_frame_mouse_status = Some((
3555 mouse_pos.into(),
3556 ClickAim::RightResize,
3557 [
3558 mouse_pos.x - position[0],
3559 mouse_pos.y - position[1],
3560 ],
3561 ))
3562 };
3563 if size[0] > resource_panel.min_size[0]
3564 && (resource_panel.max_size.is_none()
3565 || size[0]
3566 < resource_panel.max_size.unwrap()[0])
3567 {
3568 ctx.set_cursor_icon(CursorIcon::ResizeHorizontal);
3569 } else if resource_panel.max_size.is_some()
3570 && size[0] >= resource_panel.max_size.unwrap()[0]
3571 {
3572 ctx.set_cursor_icon(CursorIcon::ResizeWest);
3573 } else {
3574 ctx.set_cursor_icon(CursorIcon::ResizeEast);
3575 };
3576 }
3577 [true, false] => {
3578 if resource_panel.last_frame_mouse_status.is_none()
3579 && ui.input(|i| i.pointer.primary_pressed())
3580 {
3581 resource_panel.last_frame_mouse_status = Some((
3582 mouse_pos.into(),
3583 ClickAim::TopResize,
3584 [
3585 mouse_pos.x - position[0],
3586 mouse_pos.y - position[1],
3587 ],
3588 ))
3589 };
3590 if size[1] > resource_panel.min_size[1]
3591 && (resource_panel.max_size.is_none()
3592 || size[1]
3593 < resource_panel.max_size.unwrap()[1])
3594 {
3595 ctx.set_cursor_icon(CursorIcon::ResizeVertical);
3596 } else if resource_panel.max_size.is_some()
3597 && size[1] >= resource_panel.max_size.unwrap()[1]
3598 {
3599 ctx.set_cursor_icon(CursorIcon::ResizeSouth);
3600 } else {
3601 ctx.set_cursor_icon(CursorIcon::ResizeNorth);
3602 };
3603 }
3604 [false, false] => {}
3605 }
3606 }
3607 [false, true, true, false] => {
3608 match [resource_panel.resizable[1], resource_panel.resizable[2]]
3609 {
3610 [true, true] => {
3611 if resource_panel.last_frame_mouse_status.is_none()
3612 && ui.input(|i| i.pointer.primary_pressed())
3613 {
3614 resource_panel.last_frame_mouse_status = Some((
3615 mouse_pos.into(),
3616 ClickAim::LeftBottomResize,
3617 [
3618 mouse_pos.x - position[0],
3619 mouse_pos.y - position[1],
3620 ],
3621 ))
3622 };
3623 if size[0] > resource_panel.min_size[0]
3624 && (resource_panel.max_size.is_none()
3625 || size[0]
3626 < resource_panel.max_size.unwrap()[0])
3627 || size[1] > resource_panel.min_size[1]
3628 && (resource_panel.max_size.is_none()
3629 || size[1]
3630 < resource_panel.max_size.unwrap()[1])
3631 {
3632 ctx.set_cursor_icon(CursorIcon::ResizeNeSw);
3633 } else if resource_panel.max_size.is_some()
3634 && size[0] >= resource_panel.max_size.unwrap()[0]
3635 && size[1] >= resource_panel.max_size.unwrap()[1]
3636 {
3637 ctx.set_cursor_icon(CursorIcon::ResizeNorthEast);
3638 } else {
3639 ctx.set_cursor_icon(CursorIcon::ResizeSouthWest)
3640 };
3641 }
3642 [false, true] => {
3643 if resource_panel.last_frame_mouse_status.is_none()
3644 && ui.input(|i| i.pointer.primary_pressed())
3645 {
3646 resource_panel.last_frame_mouse_status = Some((
3647 mouse_pos.into(),
3648 ClickAim::LeftResize,
3649 [
3650 mouse_pos.x - position[0],
3651 mouse_pos.y - position[1],
3652 ],
3653 ))
3654 };
3655 if size[0] > resource_panel.min_size[0]
3656 && (resource_panel.max_size.is_none()
3657 || size[0]
3658 < resource_panel.max_size.unwrap()[0])
3659 {
3660 ctx.set_cursor_icon(CursorIcon::ResizeHorizontal);
3661 } else if resource_panel.max_size.is_some()
3662 && size[0] >= resource_panel.max_size.unwrap()[0]
3663 {
3664 ctx.set_cursor_icon(CursorIcon::ResizeEast);
3665 } else {
3666 ctx.set_cursor_icon(CursorIcon::ResizeWest);
3667 };
3668 }
3669 [true, false] => {
3670 if resource_panel.last_frame_mouse_status.is_none()
3671 && ui.input(|i| i.pointer.primary_pressed())
3672 {
3673 resource_panel.last_frame_mouse_status = Some((
3674 mouse_pos.into(),
3675 ClickAim::BottomResize,
3676 [
3677 mouse_pos.x - position[0],
3678 mouse_pos.y - position[1],
3679 ],
3680 ))
3681 };
3682 if size[1] > resource_panel.min_size[1]
3683 && (resource_panel.max_size.is_none()
3684 || size[1]
3685 < resource_panel.max_size.unwrap()[1])
3686 {
3687 ctx.set_cursor_icon(CursorIcon::ResizeVertical);
3688 } else if resource_panel.max_size.is_some()
3689 && size[1] >= resource_panel.max_size.unwrap()[1]
3690 {
3691 ctx.set_cursor_icon(CursorIcon::ResizeNorth);
3692 } else {
3693 ctx.set_cursor_icon(CursorIcon::ResizeSouth);
3694 };
3695 }
3696 [false, false] => {}
3697 }
3698 }
3699 _ => {}
3700 };
3701 resource_panel.last_frame_mouse_status =
3702 if resource_panel.last_frame_mouse_status.is_none()
3703 && rect.contains(mouse_pos)
3704 && ui.input(|i| i.pointer.primary_pressed())
3705 {
3706 Some((
3707 [mouse_pos.x, mouse_pos.y],
3708 ClickAim::Move,
3709 [mouse_pos.x - position[0], mouse_pos.y - position[1]],
3710 ))
3711 } else if resource_panel.last_frame_mouse_status.is_some()
3712 && !ui.input(|i| i.pointer.primary_released())
3713 {
3714 Some((
3715 [mouse_pos.x, mouse_pos.y],
3716 resource_panel.last_frame_mouse_status.unwrap().1,
3717 resource_panel.last_frame_mouse_status.unwrap().2,
3718 ))
3719 } else {
3720 None
3721 };
3722 if resource_panel.scroll_length_method[0].is_some()
3723 && x_scroll_delta != 0_f32
3724 && rect.contains(mouse_pos)
3725 {
3726 resource_panel.scrolled[0] = true;
3727 resource_panel.scroll_progress[0] = if resource_panel
3728 .scroll_progress[0]
3729 + -x_scroll_delta * resource_panel.scroll_sensitivity
3730 > resource_panel.scroll_length[0]
3731 {
3732 resource_panel.scroll_length[0]
3733 } else if resource_panel.scroll_progress[0]
3734 + -x_scroll_delta * resource_panel.scroll_sensitivity
3735 > 0_f32
3736 {
3737 resource_panel.scroll_progress[0]
3738 + -x_scroll_delta * resource_panel.scroll_sensitivity
3739 } else {
3740 0_f32
3741 };
3742 };
3743 if resource_panel.scroll_length_method[1].is_some()
3744 && y_scroll_delta != 0_f32
3745 && rect.contains(mouse_pos)
3746 {
3747 resource_panel.scrolled[1] = true;
3748 resource_panel.scroll_progress[1] = if resource_panel
3749 .scroll_progress[1]
3750 + -y_scroll_delta * resource_panel.scroll_sensitivity
3751 > resource_panel.scroll_length[1]
3752 {
3753 resource_panel.scroll_length[1]
3754 } else if resource_panel.scroll_progress[1]
3755 + -y_scroll_delta * resource_panel.scroll_sensitivity
3756 > 0_f32
3757 {
3758 resource_panel.scroll_progress[1]
3759 + -y_scroll_delta * resource_panel.scroll_sensitivity
3760 } else {
3761 0_f32
3762 };
3763 };
3764 } else if ui.input(|i| i.pointer.primary_released()) {
3765 resource_panel.last_frame_mouse_status = None;
3766 };
3767 };
3768 if let Some((mouse_pos, click_aim, offset)) =
3769 resource_panel.last_frame_mouse_status
3770 {
3771 match click_aim {
3772 ClickAim::LeftTopResize => {
3773 if position[0] - mouse_pos[0] + size[0] > resource_panel.min_size[0]
3774 && (resource_panel.max_size.is_none()
3775 || position[0] - mouse_pos[0] + size[0]
3776 < resource_panel.max_size.unwrap()[0])
3777 {
3778 position_size_config.origin_size[0] +=
3779 position[0] - mouse_pos[0];
3780 position_size_config.origin_position[0] = mouse_pos[0];
3781 } else if resource_panel.max_size.is_some()
3782 && position[0] - mouse_pos[0] + size[0]
3783 >= resource_panel.max_size.unwrap()[0]
3784 {
3785 position_size_config.origin_position[0] -=
3786 resource_panel.max_size.unwrap()[0]
3787 - position_size_config.origin_size[0];
3788 position_size_config.origin_size[0] =
3789 resource_panel.max_size.unwrap()[0];
3790 } else {
3791 position_size_config.origin_position[0] += position_size_config
3792 .origin_size[0]
3793 - resource_panel.min_size[0];
3794 position_size_config.origin_size[0] =
3795 resource_panel.min_size[0];
3796 };
3797 if position[1] - mouse_pos[1] + size[1] > resource_panel.min_size[1]
3798 && (resource_panel.max_size.is_none()
3799 || position[1] - mouse_pos[1] + size[1]
3800 < resource_panel.max_size.unwrap()[1])
3801 {
3802 position_size_config.origin_size[1] +=
3803 position[1] - mouse_pos[1];
3804 position_size_config.origin_position[1] = mouse_pos[1];
3805 } else if resource_panel.max_size.is_some()
3806 && position[1] - mouse_pos[1] + size[1]
3807 >= resource_panel.max_size.unwrap()[1]
3808 {
3809 position_size_config.origin_position[1] -=
3810 resource_panel.max_size.unwrap()[1]
3811 - position_size_config.origin_size[1];
3812 position_size_config.origin_size[1] =
3813 resource_panel.max_size.unwrap()[1];
3814 } else {
3815 position_size_config.origin_position[1] += position_size_config
3816 .origin_size[1]
3817 - resource_panel.min_size[1];
3818 position_size_config.origin_size[1] =
3819 resource_panel.min_size[1];
3820 };
3821 if size[0] > resource_panel.min_size[0]
3822 && (resource_panel.max_size.is_none()
3823 || size[0] < resource_panel.max_size.unwrap()[0])
3824 || size[1] > resource_panel.min_size[1]
3825 && (resource_panel.max_size.is_none()
3826 || size[1] < resource_panel.max_size.unwrap()[1])
3827 {
3828 ctx.set_cursor_icon(CursorIcon::ResizeNwSe);
3829 } else if resource_panel.max_size.is_some()
3830 && size[0] >= resource_panel.max_size.unwrap()[0]
3831 && size[1] >= resource_panel.max_size.unwrap()[1]
3832 {
3833 ctx.set_cursor_icon(CursorIcon::ResizeSouthEast);
3834 } else {
3835 ctx.set_cursor_icon(CursorIcon::ResizeNorthWest)
3836 };
3837 }
3838 ClickAim::RightBottomResize => {
3839 if mouse_pos[0] - position[0] > resource_panel.min_size[0]
3840 && (resource_panel.max_size.is_none()
3841 || mouse_pos[0] - position[0]
3842 < resource_panel.max_size.unwrap()[0])
3843 {
3844 position_size_config.origin_size[0] =
3845 mouse_pos[0] - position[0];
3846 } else if resource_panel.max_size.is_some()
3847 && mouse_pos[0] - position[0]
3848 >= resource_panel.max_size.unwrap()[0]
3849 {
3850 position_size_config.origin_size[0] =
3851 resource_panel.max_size.unwrap()[0];
3852 } else {
3853 position_size_config.origin_size[0] =
3854 resource_panel.min_size[0];
3855 };
3856 if mouse_pos[1] - position[1] > resource_panel.min_size[1]
3857 && (resource_panel.max_size.is_none()
3858 || mouse_pos[1] - position[1]
3859 < resource_panel.max_size.unwrap()[1])
3860 {
3861 position_size_config.origin_size[1] =
3862 mouse_pos[1] - position[1];
3863 } else if resource_panel.max_size.is_some()
3864 && mouse_pos[1] - position[1]
3865 >= resource_panel.max_size.unwrap()[1]
3866 {
3867 position_size_config.origin_size[1] =
3868 resource_panel.max_size.unwrap()[1];
3869 } else {
3870 position_size_config.origin_size[1] =
3871 resource_panel.min_size[1];
3872 };
3873 if size[0] > resource_panel.min_size[0]
3874 && (resource_panel.max_size.is_none()
3875 || size[0] < resource_panel.max_size.unwrap()[0])
3876 || size[1] > resource_panel.min_size[1]
3877 && (resource_panel.max_size.is_none()
3878 || size[1] < resource_panel.max_size.unwrap()[1])
3879 {
3880 ctx.set_cursor_icon(CursorIcon::ResizeNwSe);
3881 } else if resource_panel.max_size.is_some()
3882 && size[0] >= resource_panel.max_size.unwrap()[0]
3883 && size[1] >= resource_panel.max_size.unwrap()[1]
3884 {
3885 ctx.set_cursor_icon(CursorIcon::ResizeNorthWest);
3886 } else {
3887 ctx.set_cursor_icon(CursorIcon::ResizeSouthEast)
3888 };
3889 }
3890 ClickAim::RightTopResize => {
3891 if mouse_pos[0] - position[0] > resource_panel.min_size[0]
3892 && (resource_panel.max_size.is_none()
3893 || mouse_pos[0] - position[0]
3894 < resource_panel.max_size.unwrap()[0])
3895 {
3896 position_size_config.origin_size[0] =
3897 mouse_pos[0] - position[0];
3898 } else if resource_panel.max_size.is_some()
3899 && mouse_pos[0] - position[0]
3900 >= resource_panel.max_size.unwrap()[0]
3901 {
3902 position_size_config.origin_size[0] =
3903 resource_panel.max_size.unwrap()[0];
3904 } else {
3905 position_size_config.origin_size[0] =
3906 resource_panel.min_size[0];
3907 };
3908 if position[1] - mouse_pos[1] + size[1] > resource_panel.min_size[1]
3909 && (resource_panel.max_size.is_none()
3910 || position[1] - mouse_pos[1] + size[1]
3911 < resource_panel.max_size.unwrap()[1])
3912 {
3913 position_size_config.origin_size[1] +=
3914 position[1] - mouse_pos[1];
3915 position_size_config.origin_position[1] = mouse_pos[1];
3916 } else if resource_panel.max_size.is_some()
3917 && position[1] - mouse_pos[1] + size[1]
3918 >= resource_panel.max_size.unwrap()[1]
3919 {
3920 position_size_config.origin_position[1] -=
3921 resource_panel.max_size.unwrap()[1]
3922 - position_size_config.origin_size[1];
3923 position_size_config.origin_size[1] =
3924 resource_panel.max_size.unwrap()[1];
3925 } else {
3926 position_size_config.origin_position[1] += position_size_config
3927 .origin_size[1]
3928 - resource_panel.min_size[1];
3929 position_size_config.origin_size[1] =
3930 resource_panel.min_size[1];
3931 };
3932 if size[0] > resource_panel.min_size[0]
3933 && (resource_panel.max_size.is_none()
3934 || size[0] < resource_panel.max_size.unwrap()[0])
3935 || size[1] > resource_panel.min_size[1]
3936 && (resource_panel.max_size.is_none()
3937 || size[1] < resource_panel.max_size.unwrap()[1])
3938 {
3939 ctx.set_cursor_icon(CursorIcon::ResizeNeSw);
3940 } else if resource_panel.max_size.is_some()
3941 && size[0] >= resource_panel.max_size.unwrap()[0]
3942 && size[1] >= resource_panel.max_size.unwrap()[1]
3943 {
3944 ctx.set_cursor_icon(CursorIcon::ResizeSouthWest);
3945 } else {
3946 ctx.set_cursor_icon(CursorIcon::ResizeNorthEast)
3947 };
3948 }
3949 ClickAim::LeftBottomResize => {
3950 if position[0] - mouse_pos[0] + size[0] > resource_panel.min_size[0]
3951 && (resource_panel.max_size.is_none()
3952 || position[0] - mouse_pos[0] + size[0]
3953 < resource_panel.max_size.unwrap()[0])
3954 {
3955 position_size_config.origin_size[0] +=
3956 position[0] - mouse_pos[0];
3957 position_size_config.origin_position[0] = mouse_pos[0];
3958 } else if resource_panel.max_size.is_some()
3959 && position[0] - mouse_pos[0] + size[0]
3960 >= resource_panel.max_size.unwrap()[0]
3961 {
3962 position_size_config.origin_position[0] -=
3963 resource_panel.max_size.unwrap()[0]
3964 - position_size_config.origin_size[0];
3965 position_size_config.origin_size[0] =
3966 resource_panel.max_size.unwrap()[0];
3967 } else {
3968 position_size_config.origin_position[0] += position_size_config
3969 .origin_size[0]
3970 - resource_panel.min_size[0];
3971 position_size_config.origin_size[0] =
3972 resource_panel.min_size[0];
3973 };
3974 if mouse_pos[1] - position[1] > resource_panel.min_size[1]
3975 && (resource_panel.max_size.is_none()
3976 || mouse_pos[1] - position[1]
3977 < resource_panel.max_size.unwrap()[1])
3978 {
3979 position_size_config.origin_size[1] =
3980 mouse_pos[1] - position[1];
3981 } else if resource_panel.max_size.is_some()
3982 && mouse_pos[1] - position[1]
3983 >= resource_panel.max_size.unwrap()[1]
3984 {
3985 position_size_config.origin_size[1] =
3986 resource_panel.max_size.unwrap()[1];
3987 } else {
3988 position_size_config.origin_size[1] =
3989 resource_panel.min_size[1];
3990 };
3991 if size[0] > resource_panel.min_size[0]
3992 && (resource_panel.max_size.is_none()
3993 || size[0] < resource_panel.max_size.unwrap()[0])
3994 || size[1] > resource_panel.min_size[1]
3995 && (resource_panel.max_size.is_none()
3996 || size[1] < resource_panel.max_size.unwrap()[1])
3997 {
3998 ctx.set_cursor_icon(CursorIcon::ResizeNeSw);
3999 } else if resource_panel.max_size.is_some()
4000 && size[0] >= resource_panel.max_size.unwrap()[0]
4001 && size[1] >= resource_panel.max_size.unwrap()[1]
4002 {
4003 ctx.set_cursor_icon(CursorIcon::ResizeNorthEast);
4004 } else {
4005 ctx.set_cursor_icon(CursorIcon::ResizeSouthWest)
4006 };
4007 }
4008 ClickAim::TopResize => {
4009 if position[1] - mouse_pos[1] + size[1] > resource_panel.min_size[1]
4010 && (resource_panel.max_size.is_none()
4011 || position[1] - mouse_pos[1] + size[1]
4012 < resource_panel.max_size.unwrap()[1])
4013 {
4014 position_size_config.origin_size[1] +=
4015 position[1] - mouse_pos[1];
4016 position_size_config.origin_position[1] = mouse_pos[1];
4017 ctx.set_cursor_icon(CursorIcon::ResizeVertical);
4018 } else if resource_panel.max_size.is_some()
4019 && position[1] - mouse_pos[1] + size[1]
4020 >= resource_panel.max_size.unwrap()[1]
4021 {
4022 position_size_config.origin_position[1] -=
4023 resource_panel.max_size.unwrap()[1]
4024 - position_size_config.origin_size[1];
4025 position_size_config.origin_size[1] =
4026 resource_panel.max_size.unwrap()[1];
4027 ctx.set_cursor_icon(CursorIcon::ResizeSouth);
4028 } else {
4029 position_size_config.origin_position[1] += position_size_config
4030 .origin_size[1]
4031 - resource_panel.min_size[1];
4032 position_size_config.origin_size[1] =
4033 resource_panel.min_size[1];
4034 ctx.set_cursor_icon(CursorIcon::ResizeNorth);
4035 };
4036 }
4037 ClickAim::BottomResize => {
4038 if mouse_pos[1] - position[1] > resource_panel.min_size[1]
4039 && (resource_panel.max_size.is_none()
4040 || mouse_pos[1] - position[1]
4041 < resource_panel.max_size.unwrap()[1])
4042 {
4043 position_size_config.origin_size[1] =
4044 mouse_pos[1] - position[1];
4045 ctx.set_cursor_icon(CursorIcon::ResizeVertical);
4046 } else if resource_panel.max_size.is_some()
4047 && mouse_pos[1] - position[1]
4048 >= resource_panel.max_size.unwrap()[1]
4049 {
4050 position_size_config.origin_size[1] =
4051 resource_panel.max_size.unwrap()[1];
4052 ctx.set_cursor_icon(CursorIcon::ResizeNorth);
4053 } else {
4054 position_size_config.origin_size[1] =
4055 resource_panel.min_size[1];
4056 ctx.set_cursor_icon(CursorIcon::ResizeSouth);
4057 };
4058 }
4059 ClickAim::LeftResize => {
4060 if position[0] - mouse_pos[0] + size[0] > resource_panel.min_size[0]
4061 && (resource_panel.max_size.is_none()
4062 || position[0] - mouse_pos[0] + size[0]
4063 < resource_panel.max_size.unwrap()[0])
4064 {
4065 position_size_config.origin_size[0] +=
4066 position[0] - mouse_pos[0];
4067 position_size_config.origin_position[0] = mouse_pos[0];
4068 ctx.set_cursor_icon(CursorIcon::ResizeHorizontal);
4069 } else if resource_panel.max_size.is_some()
4070 && position[0] - mouse_pos[0] + size[0]
4071 >= resource_panel.max_size.unwrap()[0]
4072 {
4073 position_size_config.origin_position[0] -=
4074 resource_panel.max_size.unwrap()[0]
4075 - position_size_config.origin_size[0];
4076 position_size_config.origin_size[0] =
4077 resource_panel.max_size.unwrap()[0];
4078 ctx.set_cursor_icon(CursorIcon::ResizeEast);
4079 } else {
4080 position_size_config.origin_position[0] += position_size_config
4081 .origin_size[0]
4082 - resource_panel.min_size[0];
4083 position_size_config.origin_size[0] =
4084 resource_panel.min_size[0];
4085 ctx.set_cursor_icon(CursorIcon::ResizeWest);
4086 };
4087 }
4088 ClickAim::RightResize => {
4089 if mouse_pos[0] - position[0] > resource_panel.min_size[0]
4090 && (resource_panel.max_size.is_none()
4091 || mouse_pos[0] - position[0]
4092 < resource_panel.max_size.unwrap()[0])
4093 {
4094 position_size_config.origin_size[0] =
4095 mouse_pos[0] - position[0];
4096 ctx.set_cursor_icon(CursorIcon::ResizeHorizontal);
4097 } else if resource_panel.max_size.is_some()
4098 && mouse_pos[0] - position[0]
4099 >= resource_panel.max_size.unwrap()[0]
4100 {
4101 position_size_config.origin_size[0] =
4102 resource_panel.max_size.unwrap()[0];
4103 ctx.set_cursor_icon(CursorIcon::ResizeWest);
4104 } else {
4105 position_size_config.origin_size[0] =
4106 resource_panel.min_size[0];
4107 ctx.set_cursor_icon(CursorIcon::ResizeEast);
4108 };
4109 }
4110 ClickAim::Move => {
4111 ctx.set_cursor_icon(match resource_panel.movable {
4112 [true, true] => CursorIcon::Move,
4113 [true, false] => CursorIcon::ResizeColumn,
4114 [false, true] => CursorIcon::ResizeRow,
4115 [false, false] => CursorIcon::NotAllowed,
4116 });
4117 if resource_panel.movable[0] {
4118 position_size_config.origin_position[0] =
4119 mouse_pos[0] - offset[0];
4120 };
4121 if resource_panel.movable[1] {
4122 position_size_config.origin_position[1] =
4123 mouse_pos[1] - offset[1];
4124 };
4125 }
4126 };
4127 };
4128 [position, size] = position_size_processor(position_size_config, ctx);
4129 let background_type = match background.background_type.clone() {
4130 BackgroundType::CustomRect(config) => BackgroundType::CustomRect(
4131 config
4132 .position_size_config(Some(position_size_config))
4133 .hidden(Some(resource_panel.hidden)),
4134 ),
4135 BackgroundType::Image(config) => BackgroundType::Image(
4136 config
4137 .position_size_config(Some(position_size_config))
4138 .hidden(Some(resource_panel.hidden)),
4139 ),
4140 };
4141 self.replace_resource(
4142 &format!("{}Background", &id.name),
4143 background.clone().background_type(&background_type).clone(),
4144 )?;
4145 self.use_resource(
4146 &RustConstructorId {
4147 name: format!("{}Background", &id.name),
4148 discern_type: "Background".to_string(),
4149 },
4150 ui,
4151 ctx,
4152 )?;
4153 type PointList = Vec<([f32; 2], [f32; 2], [bool; 2], Option<String>)>;
4154 let mut resource_point_list: PointList = Vec::new();
4155 let mut use_resource_list = Vec::new();
4156 let mut replace_resource_list = Vec::new();
4157 for rcr in &self.rust_constructor_resource {
4158 if self
4159 .basic_front_resource_list
4160 .contains(&rcr.id.discern_type)
4161 && let Some(panel_name) =
4162 get_tag("panel_name", &rcr.content.display_tags())
4163 && panel_name.1 == id.name
4164 {
4165 if let [Some(citer_name), Some(citer_type)] = [
4166 get_tag("citer_name", &rcr.content.display_tags()),
4167 get_tag("citer_type", &rcr.content.display_tags()),
4168 ] {
4169 if !use_resource_list
4170 .iter()
4171 .any(|x| x == &[citer_name.1.clone(), citer_type.1.clone()])
4172 {
4173 use_resource_list.push([citer_name.1, citer_type.1]);
4174 };
4175 } else if !use_resource_list
4176 .iter()
4177 .any(|x| x == &[rcr.id.name.clone(), rcr.id.discern_type.clone()])
4178 {
4179 use_resource_list
4180 .push([rcr.id.name.clone(), rcr.id.discern_type.clone()]);
4181 };
4182 let mut basic_front_resource: Box<dyn BasicFrontResource> =
4183 match &*rcr.id.discern_type {
4184 "Image" => {
4185 Box::new(downcast_resource::<Image>(&*rcr.content)?.clone())
4186 }
4187 "Text" => {
4188 Box::new(downcast_resource::<Text>(&*rcr.content)?.clone())
4189 }
4190 "CustomRect" => Box::new(
4191 downcast_resource::<CustomRect>(&*rcr.content)?.clone(),
4192 ),
4193 _ => {
4194 unreachable!()
4195 }
4196 };
4197 if !resource_panel
4198 .resource_storage
4199 .iter()
4200 .any(|x| x.id == rcr.id)
4201 {
4202 resource_panel.resource_storage.push(PanelStorage {
4203 id: rcr.id.clone(),
4204 ignore_render_layer: if let Some(display_info) =
4205 basic_front_resource.display_display_info()
4206 {
4207 display_info.ignore_render_layer
4208 } else {
4209 false
4210 },
4211 hidden: if let Some(display_info) =
4212 basic_front_resource.display_display_info()
4213 {
4214 display_info.hidden
4215 } else {
4216 false
4217 },
4218 });
4219 };
4220 let enable_scrolling = [
4221 get_tag("disable_x_scrolling", &rcr.content.display_tags())
4222 .is_none(),
4223 get_tag("disable_y_scrolling", &rcr.content.display_tags())
4224 .is_none(),
4225 ];
4226 let offset = basic_front_resource.display_position_size_config().offset;
4227 basic_front_resource.modify_position_size_config(
4228 basic_front_resource
4229 .display_position_size_config()
4230 .x_location_grid(0_f32, 0_f32)
4231 .y_location_grid(0_f32, 0_f32)
4232 .x_size_grid(0_f32, 0_f32)
4233 .y_size_grid(0_f32, 0_f32)
4234 .offset(
4235 if enable_scrolling[0] {
4236 -resource_panel.scroll_progress[0]
4237 } else {
4238 offset[0]
4239 },
4240 if enable_scrolling[1] {
4241 -resource_panel.scroll_progress[1]
4242 } else {
4243 offset[1]
4244 },
4245 ),
4246 );
4247 let mut layout = resource_panel.overall_layout;
4248 for custom_layout in &resource_panel.custom_layout {
4249 match custom_layout {
4250 CustomPanelLayout::Id(layout_id, panel_layout) => {
4251 if rcr.id.cmp(layout_id) == Ordering::Equal {
4252 layout = *panel_layout;
4253 break;
4254 };
4255 }
4256 CustomPanelLayout::Type(layout_type, panel_layout) => {
4257 if *layout_type == rcr.id.discern_type {
4258 layout = *panel_layout;
4259 }
4260 }
4261 };
4262 }
4263 let panel_layout_group = if let Some(panel_layout_group) =
4264 get_tag("panel_layout_group", &basic_front_resource.display_tags())
4265 {
4266 Some(panel_layout_group.1)
4267 } else {
4268 None
4269 };
4270 match layout.panel_margin {
4271 PanelMargin::Vertical(
4272 [top, bottom, left, right],
4273 move_to_bottom,
4274 ) => {
4275 let mut modify_y = 0_f32;
4276 let [default_x_position, default_y_position] =
4277 match layout.panel_location {
4278 PanelLocation::Absolute([x, y]) => {
4279 [position[0] + x, position[1] + y]
4280 }
4281 PanelLocation::Relative([x, y]) => [
4282 position[0]
4283 + if x[1] != 0_f32 {
4284 size[0] / x[1] * x[0]
4285 } else {
4286 0_f32
4287 },
4288 position[1]
4289 + if y[1] != 0_f32 {
4290 size[1] / y[1] * y[0]
4291 } else {
4292 0_f32
4293 },
4294 ],
4295 };
4296 let default_x_position = match basic_front_resource
4297 .display_position_size_config()
4298 .display_method
4299 .0
4300 {
4301 HorizontalAlign::Left => default_x_position,
4302 HorizontalAlign::Center => {
4303 default_x_position
4304 - basic_front_resource.display_size()[0] / 2.0
4305 }
4306 HorizontalAlign::Right => {
4307 default_x_position
4308 - basic_front_resource.display_size()[0]
4309 }
4310 };
4311 let default_y_position = match basic_front_resource
4312 .display_position_size_config()
4313 .display_method
4314 .1
4315 {
4316 VerticalAlign::Top => default_y_position,
4317 VerticalAlign::Center => {
4318 default_y_position
4319 - basic_front_resource.display_size()[1] / 2.0
4320 }
4321 VerticalAlign::Bottom => {
4322 default_y_position
4323 - basic_front_resource.display_size()[1]
4324 }
4325 };
4326 for point in &resource_point_list {
4327 if let Some(ref point_panel_layout_group) = point.3
4328 && let Some(ref panel_layout_group) = panel_layout_group
4329 && panel_layout_group == point_panel_layout_group
4330 {
4331 continue;
4332 };
4333 if default_x_position - left < point.1[0]
4334 && default_y_position - top + modify_y < point.1[1]
4335 && default_x_position
4336 + basic_front_resource.display_size()[0]
4337 + right
4338 > point.0[0]
4339 && default_y_position
4340 + basic_front_resource.display_size()[1]
4341 + bottom
4342 + modify_y
4343 > point.0[1]
4344 {
4345 if move_to_bottom
4346 && point.1[1] - default_y_position + top > modify_y
4347 {
4348 modify_y = point.1[1] - default_y_position + top;
4349 } else if !move_to_bottom
4350 && point.0[1]
4351 - default_y_position
4352 - basic_front_resource.display_size()[1]
4353 - bottom
4354 < modify_y
4355 {
4356 modify_y = point.0[1]
4357 - default_y_position
4358 - basic_front_resource.display_size()[1]
4359 - bottom;
4360 };
4361 };
4362 }
4363 let real_x_position = match basic_front_resource
4364 .display_position_size_config()
4365 .display_method
4366 .0
4367 {
4368 HorizontalAlign::Left => default_x_position,
4369 HorizontalAlign::Center => {
4370 default_x_position
4371 + basic_front_resource.display_size()[0] / 2.0
4372 }
4373 HorizontalAlign::Right => {
4374 default_x_position
4375 + basic_front_resource.display_size()[0]
4376 }
4377 };
4378 let real_y_position = match basic_front_resource
4379 .display_position_size_config()
4380 .display_method
4381 .1
4382 {
4383 VerticalAlign::Top => default_y_position + modify_y,
4384 VerticalAlign::Center => {
4385 default_y_position
4386 + modify_y
4387 + basic_front_resource.display_size()[1] / 2.0
4388 }
4389 VerticalAlign::Bottom => {
4390 default_y_position
4391 + modify_y
4392 + basic_front_resource.display_size()[1]
4393 }
4394 };
4395 basic_front_resource.modify_position_size_config(
4396 basic_front_resource
4397 .display_position_size_config()
4398 .origin_position(
4399 real_x_position
4400 + left
4401 + resource_panel.inner_margin[2],
4402 real_y_position
4403 + top
4404 + resource_panel.inner_margin[0],
4405 ),
4406 );
4407 replace_resource_list.push((
4408 basic_front_resource.display_position_size_config(),
4409 [rcr.id.name.clone(), rcr.id.discern_type.clone()],
4410 ));
4411 resource_point_list.push((
4412 [real_x_position - left, real_y_position - top],
4413 [
4414 real_x_position
4415 + basic_front_resource.display_size()[0]
4416 + right,
4417 real_y_position
4418 + basic_front_resource.display_size()[1]
4419 + bottom,
4420 ],
4421 enable_scrolling,
4422 panel_layout_group,
4423 ));
4424 }
4425 PanelMargin::Horizontal(
4426 [top, bottom, left, right],
4427 move_to_right,
4428 ) => {
4429 let mut modify_x = 0_f32;
4430 let [default_x_position, default_y_position] =
4431 match layout.panel_location {
4432 PanelLocation::Absolute([x, y]) => {
4433 [position[0] + x, position[1] + y]
4434 }
4435 PanelLocation::Relative([x, y]) => [
4436 position[0]
4437 + if x[1] != 0_f32 {
4438 size[0] / x[1] * x[0]
4439 } else {
4440 0_f32
4441 },
4442 position[1]
4443 + if y[1] != 0_f32 {
4444 size[1] / y[1] * y[0]
4445 } else {
4446 0_f32
4447 },
4448 ],
4449 };
4450 let default_x_position = match basic_front_resource
4451 .display_position_size_config()
4452 .display_method
4453 .0
4454 {
4455 HorizontalAlign::Left => default_x_position,
4456 HorizontalAlign::Center => {
4457 default_x_position
4458 - basic_front_resource.display_size()[0] / 2.0
4459 }
4460 HorizontalAlign::Right => {
4461 default_x_position
4462 - basic_front_resource.display_size()[0]
4463 }
4464 };
4465 let default_y_position = match basic_front_resource
4466 .display_position_size_config()
4467 .display_method
4468 .1
4469 {
4470 VerticalAlign::Top => default_y_position,
4471 VerticalAlign::Center => {
4472 default_y_position
4473 - basic_front_resource.display_size()[1] / 2.0
4474 }
4475 VerticalAlign::Bottom => {
4476 default_y_position
4477 - basic_front_resource.display_size()[1]
4478 }
4479 };
4480 for point in &resource_point_list {
4481 if let Some(ref point_panel_layout_group) = point.3
4482 && let Some(ref panel_layout_group) = panel_layout_group
4483 && panel_layout_group == point_panel_layout_group
4484 {
4485 continue;
4486 };
4487 if default_x_position - left + modify_x < point.1[0]
4488 && default_y_position - top < point.1[1]
4489 && default_x_position
4490 + basic_front_resource.display_size()[0]
4491 + right
4492 + modify_x
4493 > point.0[0]
4494 && default_y_position
4495 + basic_front_resource.display_size()[1]
4496 + bottom
4497 > point.0[1]
4498 {
4499 if move_to_right
4500 && point.1[0] - default_x_position + left > modify_x
4501 {
4502 modify_x = point.1[0] - default_x_position + left;
4503 } else if !move_to_right
4504 && point.0[0]
4505 - default_x_position
4506 - basic_front_resource.display_size()[0]
4507 - right
4508 < modify_x
4509 {
4510 modify_x = point.0[0]
4511 - default_x_position
4512 - basic_front_resource.display_size()[0]
4513 - right;
4514 };
4515 };
4516 }
4517 let real_x_position = match basic_front_resource
4518 .display_position_size_config()
4519 .display_method
4520 .0
4521 {
4522 HorizontalAlign::Left => default_x_position + modify_x,
4523 HorizontalAlign::Center => {
4524 default_x_position
4525 + modify_x
4526 + basic_front_resource.display_size()[0] / 2.0
4527 }
4528 HorizontalAlign::Right => {
4529 default_x_position
4530 + modify_x
4531 + basic_front_resource.display_size()[0]
4532 }
4533 };
4534 let real_y_position = match basic_front_resource
4535 .display_position_size_config()
4536 .display_method
4537 .1
4538 {
4539 VerticalAlign::Top => default_y_position,
4540 VerticalAlign::Center => {
4541 default_y_position
4542 + basic_front_resource.display_size()[1] / 2.0
4543 }
4544 VerticalAlign::Bottom => {
4545 default_y_position
4546 + basic_front_resource.display_size()[1]
4547 }
4548 };
4549 basic_front_resource.modify_position_size_config(
4550 basic_front_resource
4551 .display_position_size_config()
4552 .origin_position(
4553 real_x_position
4554 + left
4555 + resource_panel.inner_margin[2],
4556 real_y_position
4557 + top
4558 + resource_panel.inner_margin[0],
4559 ),
4560 );
4561 replace_resource_list.push((
4562 basic_front_resource.display_position_size_config(),
4563 [rcr.id.name.clone(), rcr.id.discern_type.clone()],
4564 ));
4565 resource_point_list.push((
4566 [real_x_position - left, real_y_position - top],
4567 [
4568 real_x_position
4569 + basic_front_resource.display_size()[0]
4570 + right,
4571 real_y_position
4572 + basic_front_resource.display_size()[1]
4573 + bottom,
4574 ],
4575 enable_scrolling,
4576 panel_layout_group,
4577 ));
4578 }
4579 PanelMargin::None([top, bottom, left, right], influence_layout) => {
4580 let [default_x_position, default_y_position] =
4581 match layout.panel_location {
4582 PanelLocation::Absolute([x, y]) => {
4583 [position[0] + x, position[1] + y]
4584 }
4585 PanelLocation::Relative([x, y]) => [
4586 position[0]
4587 + if x[1] != 0_f32 {
4588 size[0] / x[1] * x[0]
4589 } else {
4590 0_f32
4591 },
4592 position[1]
4593 + if y[1] != 0_f32 {
4594 size[1] / y[1] * y[0]
4595 } else {
4596 0_f32
4597 },
4598 ],
4599 };
4600 basic_front_resource.modify_position_size_config(
4601 basic_front_resource
4602 .display_position_size_config()
4603 .origin_position(
4604 default_x_position
4605 + left
4606 + resource_panel.inner_margin[2],
4607 default_y_position
4608 + top
4609 + resource_panel.inner_margin[0],
4610 ),
4611 );
4612 replace_resource_list.push((
4613 basic_front_resource.display_position_size_config(),
4614 [rcr.id.name.clone(), rcr.id.discern_type.clone()],
4615 ));
4616 if influence_layout {
4617 resource_point_list.push((
4618 [default_x_position - left, default_y_position - top],
4619 [
4620 default_x_position
4621 + basic_front_resource.display_size()[0]
4622 + right,
4623 default_y_position
4624 + basic_front_resource.display_size()[1]
4625 + bottom,
4626 ],
4627 enable_scrolling,
4628 panel_layout_group,
4629 ));
4630 };
4631 }
4632 };
4633 };
4634 }
4635 for (new_position_size_config, [name, discern_type]) in replace_resource_list {
4636 let id = RustConstructorId {
4637 name: name.clone(),
4638 discern_type: discern_type.clone(),
4639 };
4640 let default_storage = if let Some(resource_storage) =
4641 resource_panel.resource_storage.iter().find(|x| x.id == id)
4642 {
4643 [
4644 true,
4645 resource_storage.ignore_render_layer,
4646 resource_storage.hidden,
4647 ]
4648 } else {
4649 [false, true, true]
4650 };
4651 let basic_front_resource = self.get_basic_front_resource_mut(&id)?;
4652 basic_front_resource.modify_position_size_config(new_position_size_config);
4653 basic_front_resource.modify_clip_rect(Some(
4654 position_size_config
4655 .origin_size(
4656 position_size_config.origin_size[0]
4657 - resource_panel.inner_margin[2]
4658 - resource_panel.inner_margin[3],
4659 position_size_config.origin_size[1]
4660 - resource_panel.inner_margin[0]
4661 - resource_panel.inner_margin[1],
4662 )
4663 .origin_position(
4664 position_size_config.origin_position[0]
4665 + resource_panel.inner_margin[2],
4666 position_size_config.origin_position[1]
4667 + resource_panel.inner_margin[0],
4668 ),
4669 ));
4670 basic_front_resource.modify_display_info({
4671 let mut display_info =
4672 basic_front_resource.display_display_info().unwrap();
4673 display_info.ignore_render_layer =
4674 if (resource_panel.last_frame_mouse_status.is_some()
4675 || [x_scroll_delta, y_scroll_delta].iter().any(|x| *x != 0_f32))
4676 && resource_get_focus[1]
4677 {
4678 true
4679 } else if default_storage[0] {
4680 default_storage[1]
4681 } else {
4682 display_info.ignore_render_layer
4683 };
4684 display_info.hidden = if resource_panel.hidden {
4685 true
4686 } else if default_storage[0] {
4687 default_storage[2]
4688 } else {
4689 display_info.hidden
4690 };
4691 display_info
4692 });
4693 }
4694 for info in use_resource_list {
4695 self.use_resource(
4696 &RustConstructorId {
4697 name: info[0].clone(),
4698 discern_type: info[1].clone(),
4699 },
4700 ui,
4701 ctx,
4702 )?;
4703 }
4704 let mut resource_length = [None, None];
4705 for point in resource_point_list {
4706 resource_length = [
4707 if resource_length[0].is_none()
4708 || resource_length[0].is_some()
4709 && point.1[0] > resource_length[0].unwrap()
4710 && point.2[0]
4711 {
4712 Some(point.1[0])
4713 } else {
4714 resource_length[0]
4715 },
4716 if resource_length[1].is_none()
4717 || resource_length[1].is_some()
4718 && point.1[1] > resource_length[1].unwrap()
4719 && point.2[1]
4720 {
4721 Some(point.1[1])
4722 } else {
4723 resource_length[1]
4724 },
4725 ]
4726 }
4727 if let Some(horizontal_scroll_length_method) =
4728 resource_panel.scroll_length_method[0]
4729 {
4730 let margin = match resource_panel.overall_layout.panel_margin {
4731 PanelMargin::Horizontal([_, _, left, right], _) => left + right,
4732 PanelMargin::Vertical([_, _, left, right], _) => left + right,
4733 PanelMargin::None([_, _, left, right], _) => left + right,
4734 };
4735 resource_panel.scroll_length[0] = match horizontal_scroll_length_method {
4736 ScrollLengthMethod::Fixed(fixed_length) => fixed_length,
4737 ScrollLengthMethod::AutoFit(expand) => {
4738 if let Some(max) = resource_length[0] {
4739 let width = max - position[0];
4740 if width - size[0]
4741 + expand
4742 + margin
4743 + resource_panel.inner_margin[3]
4744 + resource_panel.inner_margin[2]
4745 > 0_f32
4746 {
4747 width - size[0]
4748 + expand
4749 + margin
4750 + resource_panel.inner_margin[3]
4751 + resource_panel.inner_margin[2]
4752 } else {
4753 0_f32
4754 }
4755 } else {
4756 0_f32
4757 }
4758 }
4759 };
4760 if resource_panel.scroll_progress[0] > resource_panel.scroll_length[0] {
4761 resource_panel.scroll_progress[0] = resource_panel.scroll_length[0];
4762 };
4763 };
4764 if let Some(vertical_scroll_length_method) =
4765 resource_panel.scroll_length_method[1]
4766 {
4767 let margin = match resource_panel.overall_layout.panel_margin {
4768 PanelMargin::Horizontal([top, bottom, _, _], _) => top + bottom,
4769 PanelMargin::Vertical([top, bottom, _, _], _) => top + bottom,
4770 PanelMargin::None([top, bottom, _, _], _) => top + bottom,
4771 };
4772 resource_panel.scroll_length[1] = match vertical_scroll_length_method {
4773 ScrollLengthMethod::Fixed(fixed_length) => fixed_length,
4774 ScrollLengthMethod::AutoFit(expand) => {
4775 if let Some(max) = resource_length[1] {
4776 let height = max - position[1];
4777 if height - size[1]
4778 + expand
4779 + margin
4780 + resource_panel.inner_margin[1]
4781 + resource_panel.inner_margin[0]
4782 > 0_f32
4783 {
4784 height - size[1]
4785 + expand
4786 + margin
4787 + resource_panel.inner_margin[1]
4788 + resource_panel.inner_margin[0]
4789 } else {
4790 0_f32
4791 }
4792 } else {
4793 0_f32
4794 }
4795 }
4796 };
4797 if resource_panel.scroll_progress[1] > resource_panel.scroll_length[1] {
4798 resource_panel.scroll_progress[1] = resource_panel.scroll_length[1];
4799 };
4800 };
4801 match resource_panel.scroll_bar_display_method {
4802 ScrollBarDisplayMethod::Always(ref config, margin, width) => {
4803 let line_length = if resource_panel.scroll_length[1] == 0_f32 {
4804 (size[0] - margin[0] * 2_f32)
4805 * (size[0] / (resource_panel.scroll_length[0] + size[0]))
4806 } else {
4807 (size[0] - width - margin[1] - margin[0] * 2_f32)
4808 * (size[0] / (resource_panel.scroll_length[0] + size[0]))
4809 };
4810 let line_position = if resource_panel.scroll_length[1] == 0_f32 {
4811 position[0]
4812 + margin[0]
4813 + (size[0] - margin[0] * 2_f32 - line_length)
4814 * (resource_panel.scroll_progress[0]
4815 / resource_panel.scroll_length[0])
4816 } else {
4817 position[0]
4818 + margin[0]
4819 + (size[0]
4820 - margin[0] * 2_f32
4821 - width
4822 - margin[1]
4823 - line_length)
4824 * (resource_panel.scroll_progress[0]
4825 / resource_panel.scroll_length[0])
4826 };
4827 self.replace_resource(
4828 &format!("{}XScroll", &id.name),
4829 background.clone().background_type(&match config.clone() {
4830 BackgroundType::CustomRect(config) => {
4831 BackgroundType::CustomRect(
4832 config
4833 .ignore_render_layer(Some(true))
4834 .hidden(Some(resource_panel.hidden))
4835 .position_size_config(Some(
4836 PositionSizeConfig::default()
4837 .display_method(
4838 HorizontalAlign::Left,
4839 VerticalAlign::Bottom,
4840 )
4841 .origin_position(
4842 line_position,
4843 position[1] + size[1] - margin[1],
4844 )
4845 .origin_size(line_length, width),
4846 )),
4847 )
4848 }
4849 BackgroundType::Image(config) => BackgroundType::Image(
4850 config
4851 .ignore_render_layer(Some(true))
4852 .hidden(Some(resource_panel.hidden))
4853 .position_size_config(Some(
4854 PositionSizeConfig::default()
4855 .display_method(
4856 HorizontalAlign::Left,
4857 VerticalAlign::Bottom,
4858 )
4859 .origin_position(
4860 line_position,
4861 position[1] + size[1] - margin[1],
4862 )
4863 .origin_size(line_length, width),
4864 )),
4865 ),
4866 }),
4867 )?;
4868 self.use_resource(
4869 &RustConstructorId {
4870 name: format!("{}XScroll", &id.name),
4871 discern_type: "Background".to_string(),
4872 },
4873 ui,
4874 ctx,
4875 )?;
4876 let line_length = if resource_panel.scroll_length[0] == 0_f32 {
4877 (size[1] - margin[0] * 2_f32)
4878 * (size[1] / (resource_panel.scroll_length[1] + size[1]))
4879 } else {
4880 (size[1] - width - margin[1] - margin[0] * 2_f32)
4881 * (size[1] / (resource_panel.scroll_length[1] + size[1]))
4882 };
4883 let line_position = if resource_panel.scroll_length[0] == 0_f32 {
4884 position[1]
4885 + margin[0]
4886 + (size[1] - margin[0] * 2_f32 - line_length)
4887 * (resource_panel.scroll_progress[1]
4888 / resource_panel.scroll_length[1])
4889 } else {
4890 position[1]
4891 + margin[0]
4892 + (size[1]
4893 - margin[0] * 2_f32
4894 - width
4895 - margin[1]
4896 - line_length)
4897 * (resource_panel.scroll_progress[1]
4898 / resource_panel.scroll_length[1])
4899 };
4900 self.replace_resource(
4901 &format!("{}YScroll", &id.name),
4902 background.background_type(&match config.clone() {
4903 BackgroundType::CustomRect(config) => {
4904 BackgroundType::CustomRect(
4905 config
4906 .ignore_render_layer(Some(true))
4907 .hidden(Some(resource_panel.hidden))
4908 .position_size_config(Some(
4909 PositionSizeConfig::default()
4910 .display_method(
4911 HorizontalAlign::Right,
4912 VerticalAlign::Top,
4913 )
4914 .origin_position(
4915 position[0] + size[0] - margin[1],
4916 line_position,
4917 )
4918 .origin_size(width, line_length),
4919 )),
4920 )
4921 }
4922 BackgroundType::Image(config) => BackgroundType::Image(
4923 config
4924 .ignore_render_layer(Some(true))
4925 .hidden(Some(resource_panel.hidden))
4926 .position_size_config(Some(
4927 PositionSizeConfig::default()
4928 .display_method(
4929 HorizontalAlign::Right,
4930 VerticalAlign::Top,
4931 )
4932 .origin_position(
4933 position[0] + size[0] - margin[1],
4934 line_position,
4935 )
4936 .origin_size(width, line_length),
4937 )),
4938 ),
4939 }),
4940 )?;
4941 self.use_resource(
4942 &RustConstructorId {
4943 name: format!("{}YScroll", &id.name),
4944 discern_type: "Background".to_string(),
4945 },
4946 ui,
4947 ctx,
4948 )?;
4949 }
4950 ScrollBarDisplayMethod::OnlyScroll(ref config, margin, width) => {
4951 resource_panel.scroll_bar_alpha[0] = if resource_panel.scrolled[0] {
4952 self.reset_split_time(&format!(
4953 "{}ScrollBarXAlphaStart",
4954 &id.name
4955 ))?;
4956 255
4957 } else if self.timer.now_time
4958 - self
4959 .get_split_time(&format!("{}ScrollBarXAlphaStart", &id.name))?
4960 [0]
4961 >= 1_f32
4962 && self.timer.now_time
4963 - self
4964 .get_split_time(&format!("{}ScrollBarXAlpha", &id.name))?[0]
4965 >= self.tick_interval
4966 {
4967 self.reset_split_time(&format!("{}ScrollBarXAlpha", &id.name))?;
4968 resource_panel.scroll_bar_alpha[0].saturating_sub(10)
4969 } else {
4970 resource_panel.scroll_bar_alpha[0]
4971 };
4972 resource_panel.scroll_bar_alpha[1] = if resource_panel.scrolled[1] {
4973 self.reset_split_time(&format!(
4974 "{}ScrollBarYAlphaStart",
4975 &id.name
4976 ))?;
4977 255
4978 } else if self.timer.now_time
4979 - self
4980 .get_split_time(&format!("{}ScrollBarYAlphaStart", &id.name))?
4981 [0]
4982 >= 1_f32
4983 && self.timer.now_time
4984 - self
4985 .get_split_time(&format!("{}ScrollBarYAlpha", &id.name))?[0]
4986 >= self.tick_interval
4987 {
4988 self.reset_split_time(&format!("{}ScrollBarYAlpha", &id.name))?;
4989 resource_panel.scroll_bar_alpha[1].saturating_sub(10)
4990 } else {
4991 resource_panel.scroll_bar_alpha[1]
4992 };
4993 let line_length = if resource_panel.scroll_length[1] == 0_f32 {
4994 (size[0] - margin[0] * 2_f32)
4995 * (size[0] / (resource_panel.scroll_length[0] + size[0]))
4996 } else {
4997 (size[0] - width - margin[1] - margin[0] * 2_f32)
4998 * (size[0] / (resource_panel.scroll_length[0] + size[0]))
4999 };
5000 let line_position = if resource_panel.scroll_length[1] == 0_f32 {
5001 position[0]
5002 + margin[0]
5003 + (size[0] - margin[0] * 2_f32 - line_length)
5004 * (resource_panel.scroll_progress[0]
5005 / resource_panel.scroll_length[0])
5006 } else {
5007 position[0]
5008 + margin[0]
5009 + (size[0]
5010 - margin[0] * 2_f32
5011 - width
5012 - margin[1]
5013 - line_length)
5014 * (resource_panel.scroll_progress[0]
5015 / resource_panel.scroll_length[0])
5016 };
5017 self.replace_resource(
5018 &format!("{}XScroll", &id.name),
5019 background.clone().background_type(&match config.clone() {
5020 BackgroundType::CustomRect(config) => {
5021 BackgroundType::CustomRect(
5022 config
5023 .ignore_render_layer(Some(true))
5024 .hidden(Some(resource_panel.hidden))
5025 .position_size_config(Some(
5026 PositionSizeConfig::default()
5027 .display_method(
5028 HorizontalAlign::Left,
5029 VerticalAlign::Bottom,
5030 )
5031 .origin_position(
5032 line_position,
5033 position[1] + size[1] - margin[1],
5034 )
5035 .origin_size(line_length, width),
5036 ))
5037 .alpha(Some(resource_panel.scroll_bar_alpha[0]))
5038 .border_alpha(Some(
5039 resource_panel.scroll_bar_alpha[0],
5040 )),
5041 )
5042 }
5043 BackgroundType::Image(config) => BackgroundType::Image(
5044 config
5045 .ignore_render_layer(Some(true))
5046 .hidden(Some(resource_panel.hidden))
5047 .position_size_config(Some(
5048 PositionSizeConfig::default()
5049 .display_method(
5050 HorizontalAlign::Left,
5051 VerticalAlign::Bottom,
5052 )
5053 .origin_position(
5054 line_position,
5055 position[1] + size[1] - margin[1],
5056 )
5057 .origin_size(line_length, width),
5058 ))
5059 .alpha(Some(resource_panel.scroll_bar_alpha[0]))
5060 .background_alpha(Some(
5061 resource_panel.scroll_bar_alpha[0],
5062 ))
5063 .overlay_alpha(Some(
5064 resource_panel.scroll_bar_alpha[0],
5065 )),
5066 ),
5067 }),
5068 )?;
5069 self.use_resource(
5070 &RustConstructorId {
5071 name: format!("{}XScroll", &id.name),
5072 discern_type: "Background".to_string(),
5073 },
5074 ui,
5075 ctx,
5076 )?;
5077 let line_length = if resource_panel.scroll_length[0] == 0_f32 {
5078 (size[1] - margin[0] * 2_f32)
5079 * (size[1] / (resource_panel.scroll_length[1] + size[1]))
5080 } else {
5081 (size[1] - width - margin[1] - margin[0] * 2_f32)
5082 * (size[1] / (resource_panel.scroll_length[1] + size[1]))
5083 };
5084 let line_position = if resource_panel.scroll_length[0] == 0_f32 {
5085 position[1]
5086 + margin[0]
5087 + (size[1] - margin[0] * 2_f32 - line_length)
5088 * (resource_panel.scroll_progress[1]
5089 / resource_panel.scroll_length[1])
5090 } else {
5091 position[1]
5092 + margin[0]
5093 + (size[1]
5094 - margin[0] * 2_f32
5095 - width
5096 - margin[1]
5097 - line_length)
5098 * (resource_panel.scroll_progress[1]
5099 / resource_panel.scroll_length[1])
5100 };
5101 self.replace_resource(
5102 &format!("{}YScroll", &id.name),
5103 background.clone().background_type(&match config.clone() {
5104 BackgroundType::CustomRect(config) => {
5105 BackgroundType::CustomRect(
5106 config
5107 .ignore_render_layer(Some(true))
5108 .hidden(Some(resource_panel.hidden))
5109 .position_size_config(Some(
5110 PositionSizeConfig::default()
5111 .display_method(
5112 HorizontalAlign::Right,
5113 VerticalAlign::Top,
5114 )
5115 .origin_position(
5116 position[0] + size[0] - margin[1],
5117 line_position,
5118 )
5119 .origin_size(width, line_length),
5120 ))
5121 .alpha(Some(resource_panel.scroll_bar_alpha[1]))
5122 .border_alpha(Some(
5123 resource_panel.scroll_bar_alpha[1],
5124 )),
5125 )
5126 }
5127 BackgroundType::Image(config) => BackgroundType::Image(
5128 config
5129 .ignore_render_layer(Some(true))
5130 .hidden(Some(resource_panel.hidden))
5131 .position_size_config(Some(
5132 PositionSizeConfig::default()
5133 .display_method(
5134 HorizontalAlign::Right,
5135 VerticalAlign::Top,
5136 )
5137 .origin_position(
5138 position[0] + size[0] - margin[1],
5139 line_position,
5140 )
5141 .origin_size(width, line_length),
5142 ))
5143 .alpha(Some(resource_panel.scroll_bar_alpha[1]))
5144 .background_alpha(Some(
5145 resource_panel.scroll_bar_alpha[1],
5146 ))
5147 .overlay_alpha(Some(
5148 resource_panel.scroll_bar_alpha[1],
5149 )),
5150 ),
5151 }),
5152 )?;
5153 self.use_resource(
5154 &RustConstructorId {
5155 name: format!("{}YScroll", &id.name),
5156 discern_type: "Background".to_string(),
5157 },
5158 ui,
5159 ctx,
5160 )?;
5161 }
5162 ScrollBarDisplayMethod::Hidden => {}
5163 };
5164 self.replace_resource(&id.name, resource_panel.clone())?;
5165 }
5166 _ => {}
5167 };
5168 Ok(())
5169 } else {
5170 Err(RustConstructorError {
5171 error_id: "ResourceNotFound".to_string(),
5172 description: format!("Resource '{}({})' not found.", id.name, id.discern_type),
5173 })
5174 }
5175 }
5176
5177 /// Switches to a different page and resets page-specific state.
5178 ///
5179 /// 切换到不同页面并重置页面特定状态。
5180 ///
5181 /// # Arguments
5182 ///
5183 /// * `name` - The name of the page to switch to
5184 ///
5185 /// # Returns
5186 ///
5187 /// Returns `Ok(())` on success, or `Err(RustConstructorError)` if the page cannot be found.
5188 ///
5189 /// # 参数
5190 ///
5191 /// * `name` - 要切换到的页面名称
5192 ///
5193 /// # 返回值
5194 ///
5195 /// 成功时返回 `Ok(())`,如果页面无法找到则返回 `Err(RustConstructorError)`。
5196 pub fn switch_page(&mut self, name: &str) -> Result<(), RustConstructorError> {
5197 let page_data = self.get_resource_mut::<PageData>(&RustConstructorId {
5198 name: name.to_string(),
5199 discern_type: "PageData".to_string(),
5200 })?;
5201 page_data.enter_page_updated = false;
5202 self.timer.start_time = self.timer.total_time;
5203 self.current_page = name.to_string();
5204 self.update_timer();
5205 Ok(())
5206 }
5207
5208 /// Try to register all fonts in the egui context.
5209 ///
5210 /// 尝试向egui上下文中注册所有字体。
5211 ///
5212 /// This method loads and registers all fonts with the egui rendering system for
5213 /// text display.
5214 ///
5215 /// 此方法加载并注册所有字体到egui渲染系统中,用于文本显示。
5216 ///
5217 /// # Arguments
5218 ///
5219 /// * `ctx` - The egui context for font registration
5220 /// * `font_info` - Font information, including font names and paths
5221 ///
5222 /// # 参数
5223 ///
5224 /// * `ctx` - 用于字体注册的egui上下文
5225 /// * `font_info` - 字体信息,包含字体名称和路径
5226 pub fn try_register_all_fonts(&mut self, ctx: &Context, font_info: Vec<[&str; 2]>) {
5227 let mut font_definitions_amount = FontDefinitions::default();
5228 let mut loaded_fonts = Vec::new();
5229 for font_info in font_info {
5230 let mut font = FontDefinitions::default();
5231 if let Ok(font_read_data) = read(font_info[1]) {
5232 let font_data: Arc<Vec<u8>> = Arc::new(font_read_data);
5233 font.font_data.insert(
5234 font_info[0].to_owned(),
5235 Arc::new(FontData::from_owned(
5236 Arc::try_unwrap(font_data).ok().unwrap(),
5237 )),
5238 );
5239 // 将字体添加到字体列表中
5240 font.families
5241 .entry(FontFamily::Proportional)
5242 .or_default()
5243 .insert(0, font_info[0].to_owned());
5244
5245 font.families
5246 .entry(FontFamily::Monospace)
5247 .or_default()
5248 .insert(0, font_info[0].to_owned());
5249 if let Some(font_data) = font.font_data.get(font_info[0]) {
5250 font_definitions_amount
5251 .font_data
5252 .insert(font_info[0].to_string(), Arc::clone(font_data));
5253 font_definitions_amount
5254 .families
5255 .entry(FontFamily::Name(font_info[0].into()))
5256 .or_default()
5257 .push(font_info[0].to_string());
5258 // 将字体添加到字体列表中
5259 font_definitions_amount
5260 .families
5261 .entry(FontFamily::Proportional)
5262 .or_default()
5263 .insert(0, font_info[0].to_owned());
5264
5265 font_definitions_amount
5266 .families
5267 .entry(FontFamily::Monospace)
5268 .or_default()
5269 .insert(0, font_info[0].to_owned());
5270 loaded_fonts.push(font_info);
5271 };
5272 }
5273 }
5274 self.loading_fonts = loaded_fonts
5275 .iter()
5276 .map(|x| [x[0].to_string(), x[1].to_string()])
5277 .collect();
5278 ctx.set_fonts(font_definitions_amount);
5279 }
5280
5281 /// Registers all fonts with the egui context.
5282 ///
5283 /// 向egui上下文中注册所有字体。
5284 ///
5285 /// This method loads and registers all fonts with the egui rendering system for
5286 /// text display.
5287 ///
5288 /// 此方法加载并注册所有字体到egui渲染系统中,用于文本显示。
5289 ///
5290 /// # Arguments
5291 ///
5292 /// * `ctx` - The egui context for font registration
5293 /// * `font_info` - Font information, including font names and paths
5294 ///
5295 /// # Returns
5296 ///
5297 /// If the loading is successfully completed, return `Ok(())`; otherwise,
5298 /// return `Err(RustConstructorError)`.
5299 ///
5300 /// # 参数
5301 ///
5302 /// * `ctx` - 用于字体注册的egui上下文
5303 /// * `font_info` - 字体信息,包含字体名称和路径
5304 ///
5305 /// # 返回值
5306 ///
5307 /// 如果成功完成加载返回`Ok(())`,否则返回`Err(RustConstructorError)`。
5308 pub fn register_all_fonts(
5309 &mut self,
5310 ctx: &Context,
5311 font_info: Vec<[&str; 2]>,
5312 ) -> Result<(), RustConstructorError> {
5313 let mut font_definitions_amount = FontDefinitions::default();
5314 let mut loaded_fonts = Vec::new();
5315 for font_info in font_info {
5316 let mut font = FontDefinitions::default();
5317 if let Ok(font_read_data) = read(font_info[1]) {
5318 let font_data: Arc<Vec<u8>> = Arc::new(font_read_data);
5319 font.font_data.insert(
5320 font_info[0].to_owned(),
5321 Arc::new(FontData::from_owned(
5322 Arc::try_unwrap(font_data).ok().unwrap(),
5323 )),
5324 );
5325 // 将字体添加到字体列表中
5326 font.families
5327 .entry(FontFamily::Proportional)
5328 .or_default()
5329 .insert(0, font_info[0].to_owned());
5330
5331 font.families
5332 .entry(FontFamily::Monospace)
5333 .or_default()
5334 .insert(0, font_info[0].to_owned());
5335 if let Some(font_data) = font.font_data.get(font_info[0]) {
5336 font_definitions_amount
5337 .font_data
5338 .insert(font_info[0].to_string(), Arc::clone(font_data));
5339 font_definitions_amount
5340 .families
5341 .entry(FontFamily::Name(font_info[0].into()))
5342 .or_default()
5343 .push(font_info[0].to_string());
5344 // 将字体添加到字体列表中
5345 font_definitions_amount
5346 .families
5347 .entry(FontFamily::Proportional)
5348 .or_default()
5349 .insert(0, font_info[0].to_owned());
5350
5351 font_definitions_amount
5352 .families
5353 .entry(FontFamily::Monospace)
5354 .or_default()
5355 .insert(0, font_info[0].to_owned());
5356 loaded_fonts.push(font_info);
5357 };
5358 } else {
5359 return Err(RustConstructorError {
5360 error_id: "FontLoadFailed".to_string(),
5361 description: format!("Failed to load a font from the path '{}'.", font_info[1]),
5362 });
5363 }
5364 }
5365 self.loading_fonts = loaded_fonts
5366 .iter()
5367 .map(|x| [x[0].to_string(), x[1].to_string()])
5368 .collect();
5369 ctx.set_fonts(font_definitions_amount);
5370 Ok(())
5371 }
5372
5373 /// Checks if a page has completed its initial loading phase.
5374 ///
5375 /// 检查页面是否已完成首次加载。
5376 ///
5377 /// # Arguments
5378 ///
5379 /// * `name` - The name of the page to check
5380 ///
5381 /// # Returns
5382 ///
5383 /// Returns `Ok(true)` if the page has completed loading, or `Ok(false)` if the page has not completed loading.
5384 /// Returns `Err(RustConstructorError)` if the page cannot be found.
5385 ///
5386 /// # 参数
5387 ///
5388 /// * `name` - 要检查的页面名称
5389 ///
5390 /// # 返回值
5391 ///
5392 /// 如果页面已完成加载则返回 `Ok(true)`,如果未加载则返回 `Ok(false)`。
5393 /// 如果页面无法找到则返回 `Err(RustConstructorError)`。
5394 pub fn check_updated(&mut self, name: &str) -> Result<bool, RustConstructorError> {
5395 let page_data = self
5396 .get_resource::<PageData>(&RustConstructorId {
5397 name: name.to_string(),
5398 discern_type: "PageData".to_string(),
5399 })?
5400 .clone();
5401 if !page_data.change_page_updated {
5402 self.new_page_update(name)?;
5403 };
5404 Ok(page_data.change_page_updated)
5405 }
5406
5407 /// Checks if a page has completed its enter transition.
5408 ///
5409 /// 检查页面是否已完成进入过渡。
5410 ///
5411 /// # Arguments
5412 ///
5413 /// * `name` - The name of the page to check
5414 ///
5415 /// # Returns
5416 ///
5417 /// Returns `Ok(true)` if the page has completed entering, or `Ok(false)` if the page has not completed entering.
5418 /// Returns `Err(RustConstructorError)` if the page cannot be found.
5419 ///
5420 /// # 参数
5421 ///
5422 /// * `name` - 要检查的页面名称
5423 ///
5424 /// # 返回值
5425 ///
5426 /// 如果页面已完成进入则返回 `Ok(true)`,如果未过渡则返回 `Ok(false)`。
5427 /// 如果页面无法找到则返回 `Err(RustConstructorError)`。
5428 pub fn check_enter_updated(&mut self, name: &str) -> Result<bool, RustConstructorError> {
5429 let page_data = self.get_resource_mut::<PageData>(&RustConstructorId {
5430 name: name.to_string(),
5431 discern_type: "PageData".to_string(),
5432 })?;
5433 page_data.enter_page_updated = true;
5434 Ok(page_data.enter_page_updated)
5435 }
5436
5437 /// Updates when entering a new page.
5438 ///
5439 /// 进入新页面时的更新。
5440 ///
5441 /// This method is used to ensure the accuracy of the content based on the page, and the Rust Constructor will automatically call this method.
5442 ///
5443 /// 此方法用于确保基于页面的内容的准确性,Rust Constructor会自动调用此方法。
5444 ///
5445 /// # Arguments
5446 ///
5447 /// * `name` - The name of the page to be updated
5448 ///
5449 /// # Returns
5450 ///
5451 /// If the update is successful, return `Ok(())`; if the resource is not found, return `Err(RustConstructorError)`.
5452 ///
5453 /// # 参数
5454 ///
5455 /// * `name` - 要更新的页面名称
5456 ///
5457 /// # 返回值
5458 ///
5459 /// 如果更新成功则返回`Ok(())`,找不到资源则返回`Err(RustConstructorError)`。
5460 pub fn new_page_update(&mut self, name: &str) -> Result<(), RustConstructorError> {
5461 let page_data = self.get_resource_mut::<PageData>(&RustConstructorId {
5462 name: name.to_string(),
5463 discern_type: "PageData".to_string(),
5464 })?;
5465 page_data.change_page_updated = true;
5466 self.timer.start_time = self.timer.total_time;
5467 self.update_timer();
5468 Ok(())
5469 }
5470
5471 /// Updates frame timing statistics for performance monitoring.
5472 ///
5473 /// 更新帧数统计信息用于性能监控。
5474 ///
5475 /// This method maintains a rolling window of frame times and calculates
5476 /// performance metrics like frame rate.
5477 ///
5478 /// 此方法维护帧时间的滚动窗口并计算帧率等性能指标。
5479 pub fn update_frame_stats(&mut self) {
5480 let current_time = self.timer.total_time;
5481 if let Some(last) = self.last_frame_time {
5482 let delta = current_time - last;
5483 self.frame_times.push(delta);
5484 if self.frame_times.len() > 120 {
5485 let remove_count = self.frame_times.len() - 120;
5486 self.frame_times.drain(0..remove_count);
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 1.0 / (self.frame_times.iter().sum::<f32>() / self.frame_times.len() as f32)
5512 }
5513 }
5514
5515 /// Resets the split time for a specific resource.
5516 ///
5517 /// 重置特定资源的分段计时器。
5518 ///
5519 /// # Arguments
5520 ///
5521 /// * `name` - The name of the split time resource to reset
5522 ///
5523 /// # Returns
5524 ///
5525 /// Returns `Ok(())` on success, or `Err(RustConstructorError)` if the resource cannot be found.
5526 ///
5527 /// # 参数
5528 ///
5529 /// * `name` - 要重置的分段时间资源名称
5530 ///
5531 /// # 返回值
5532 ///
5533 /// 成功时返回 `Ok(())`,如果资源无法找到则返回 `Err(RustConstructorError)`。
5534 pub fn reset_split_time(&mut self, name: &str) -> Result<(), RustConstructorError> {
5535 let new_time = [self.timer.now_time, self.timer.total_time];
5536 let split_time = self.get_resource_mut::<SplitTime>(&RustConstructorId {
5537 name: name.to_string(),
5538 discern_type: "SplitTime".to_string(),
5539 })?;
5540 split_time.time = new_time;
5541 Ok(())
5542 }
5543
5544 /// Retrieves the timing information from a split time resource.
5545 ///
5546 /// 获取分段计时器资源的时间信息。
5547 ///
5548 /// # Arguments
5549 ///
5550 /// * `name` - The name of the split time resource
5551 ///
5552 /// # Returns
5553 ///
5554 /// Returns `Ok([page_runtime, total_runtime])` if found, or `Err(RustConstructorError)` if not found.
5555 ///
5556 /// # 参数
5557 ///
5558 /// * `name` - 分段计时器资源的名称
5559 ///
5560 /// # 返回值
5561 ///
5562 /// 如果找到则返回 `Ok([页面运行时间, 总运行时间])`,否则返回 `Err(RustConstructorError)`。
5563 pub fn get_split_time(&self, name: &str) -> Result<[f32; 2], RustConstructorError> {
5564 let split_time = self.get_resource::<SplitTime>(&RustConstructorId {
5565 name: name.to_string(),
5566 discern_type: "SplitTime".to_string(),
5567 })?;
5568 Ok(split_time.time)
5569 }
5570
5571 /// Updates the application timer with current timing information.
5572 ///
5573 /// 更新应用程序计时器的当前时间信息。
5574 ///
5575 /// This method updates both the total runtime and current page runtime.
5576 ///
5577 /// 此方法更新总运行时间和当前页面运行时间。
5578 pub fn update_timer(&mut self) {
5579 let elapsed = self.timer.timer.elapsed();
5580 let seconds = elapsed.as_secs();
5581 let milliseconds = elapsed.subsec_millis();
5582 self.timer.total_time = seconds as f32 + milliseconds as f32 / 1000.0;
5583 self.timer.now_time = self.timer.total_time - self.timer.start_time
5584 }
5585
5586 /// Modifies the value of a variable resource.
5587 ///
5588 /// 修改变量资源的值。
5589 ///
5590 /// # Arguments
5591 ///
5592 /// * `name` - The name of the variable resource
5593 /// * `value` - The new value to set (use `None` to clear)
5594 ///
5595 /// # Returns
5596 ///
5597 /// Returns `Ok(())` on success, or `Err(RustConstructorError)` if the resource cannot be found.
5598 ///
5599 /// # 参数
5600 ///
5601 /// * `name` - 变量资源的名称
5602 /// * `value` - 要设置的新值(使用 `None` 来清除)
5603 ///
5604 /// # 返回值
5605 ///
5606 /// 成功时返回 `Ok(())`,如果资源无法找到则返回 `Err(RustConstructorError)`。
5607 pub fn modify_variable<T: Debug + 'static>(
5608 &mut self,
5609 name: &str,
5610 value: Option<T>,
5611 ) -> Result<(), RustConstructorError> {
5612 let variable = self.get_resource_mut::<Variable<T>>(&RustConstructorId {
5613 name: name.to_string(),
5614 discern_type: "Variable".to_string(),
5615 })?;
5616 variable.value = value;
5617 Ok(())
5618 }
5619
5620 /// Take the variable out of the list.
5621 ///
5622 /// 从列表中取出变量。
5623 ///
5624 /// # Arguments
5625 ///
5626 /// * `name` - The name of the variable resource
5627 ///
5628 /// # Returns
5629 ///
5630 /// Returns `Ok(Option<T>)` on success, or `Err(RustConstructorError)` if the resource cannot be found.
5631 ///
5632 /// # 参数
5633 ///
5634 /// * `name` - 变量资源的名称
5635 ///
5636 /// # 返回值
5637 ///
5638 /// 成功时返回 `Ok(Option<T>)`,如果资源无法找到则返回 `Err(RustConstructorError)`。
5639 pub fn get_variable<T: Debug + Clone + 'static>(
5640 &self,
5641 name: &str,
5642 ) -> Result<Option<T>, RustConstructorError> {
5643 if let Ok(variable) = self.get_resource::<Variable<T>>(&RustConstructorId {
5644 name: name.to_string(),
5645 discern_type: "Variable".to_string(),
5646 }) {
5647 Ok(variable.value.clone())
5648 } else if self
5649 .check_resource_exists(&RustConstructorId {
5650 name: name.to_string(),
5651 discern_type: "Variable".to_string(),
5652 })
5653 .is_none()
5654 {
5655 Err(RustConstructorError {
5656 error_id: "ResourceNotFound".to_string(),
5657 description: format!("Resource '{name}(Variable<T>)' not found."),
5658 })
5659 } else {
5660 Err(RustConstructorError {
5661 error_id: "ResourceGenericMismatch".to_string(),
5662 description: format!(
5663 "The generic type of the resource '{name}(Variable<T>)' is mismatched."
5664 ),
5665 })
5666 }
5667 }
5668
5669 /// Modify the enable status of the switch.
5670 ///
5671 /// 修改开关的启用状态。
5672 ///
5673 /// # Arguments
5674 ///
5675 /// * `name` - The name of the switch resource
5676 /// * `enable` - The new enable status
5677 ///
5678 /// # Returns
5679 ///
5680 /// Returns `Ok(())` on success, or `Err(RustConstructorError)` if the resource cannot be found.
5681 ///
5682 /// # 参数
5683 ///
5684 /// * `name` - 开关资源的名称
5685 /// * `enable` - 新的启用状态
5686 ///
5687 /// # 返回值
5688 ///
5689 /// 成功时返回 `Ok(())`,如果资源无法找到则返回 `Err(RustConstructorError)`。
5690 pub fn set_switch_enable(
5691 &mut self,
5692 name: &str,
5693 enable: bool,
5694 ) -> Result<(), RustConstructorError> {
5695 let switch = self.get_resource_mut::<Switch>(&RustConstructorId {
5696 name: name.to_string(),
5697 discern_type: "Switch".to_string(),
5698 })?;
5699 switch.enable = enable;
5700 Ok(())
5701 }
5702
5703 /// Retrieves the current state and interaction data from a switch resource.
5704 ///
5705 /// 获取开关资源的当前状态和交互数据。
5706 ///
5707 /// # Arguments
5708 ///
5709 /// * `name` - The name of the switch resource
5710 ///
5711 /// # Returns
5712 ///
5713 /// Returns `Ok(SwitchData)` containing the switch state and interaction history,
5714 /// or `Err(RustConstructorError)` if the resource cannot be found.
5715 ///
5716 /// # 参数
5717 ///
5718 /// * `name` - 开关资源的名称
5719 ///
5720 /// # 返回值
5721 ///
5722 /// 返回包含开关状态和交互历史的 `Ok(SwitchData)`,
5723 /// 如果资源无法找到则返回 `Err(RustConstructorError)`。
5724 pub fn check_switch_data(&self, name: &str) -> Result<SwitchData, RustConstructorError> {
5725 let switch = self.get_resource::<Switch>(&RustConstructorId {
5726 name: name.to_string(),
5727 discern_type: "Switch".to_string(),
5728 })?;
5729 Ok(SwitchData {
5730 switched: switch.switched,
5731 last_frame_clicked: switch.last_frame_clicked,
5732 state: switch.state,
5733 })
5734 }
5735}