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