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