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