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