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