1use crate::{
2 font::Font,
3 material::Cull,
4 maths::{Matrix, Pose, Vec2, Vec3, units::CM},
5 prelude::*,
6 system::{Align, LogItem, LogLevel, Pivot, Text, TextFit, TextStyle},
7 ui::{Ui, UiCut},
8 util::Color128,
9};
10use std::sync::Mutex;
11
12pub const SHOW_LOG_WINDOW: &str = "Tool_ShowLogWindow";
13
14#[derive(IStepper)]
60pub struct LogWindow<'a> {
61 id: StepperId,
62 sk_info: Option<Rc<RefCell<SkInfo>>>,
63 pub enabled: bool,
64
65 pub window_pose: Pose,
66 pub x_len: f32,
67 pub y_len: f32,
68 style_diag: TextStyle,
69 style_info: TextStyle,
70 style_warn: TextStyle,
71 style_err: TextStyle,
72 pub log_log: &'a Mutex<Vec<LogItem>>,
73 log_index: f32,
74 items_size: usize,
75}
76
77unsafe impl Send for LogWindow<'_> {}
78
79impl<'a> LogWindow<'a> {
80 pub fn new(log_log: &'a Mutex<Vec<LogItem>>) -> Self {
81 let enabled = true;
82 let pose = Pose::IDENTITY;
83 let x_len = 110.0;
84 let y_len = 15.0;
85
86 let style_diag = TextStyle::from_font(Font::default(), 0.012, Color128::hsv(1.0, 0.0, 0.7, 1.0));
87 let style_info = TextStyle::from_font(Font::default(), 0.012, Color128::hsv(1.0, 0.0, 1.0, 1.0));
88 let style_warn = TextStyle::from_font(Font::default(), 0.012, Color128::hsv(0.17, 0.7, 1.0, 1.0));
89 let style_err = TextStyle::from_font(Font::default(), 0.012, Color128::hsv(1.0, 0.7, 0.7, 1.0));
90 for ui_text_style in [style_diag, style_info, style_warn, style_err] {
91 ui_text_style.get_material().face_cull(Cull::Back); }
93 Self {
94 id: "LogWindow".to_string(),
95 sk_info: None,
96 enabled,
97
98 window_pose: pose,
99 x_len,
100 y_len,
101 style_diag,
102 style_info,
103 style_warn,
104 style_err,
105 log_log,
106 log_index: 0.0,
107 items_size: 0,
108 }
109 }
110
111 fn start(&mut self) -> bool {
113 true
114 }
115
116 fn check_event(&mut self, _id: &StepperId, key: &str, value: &str) {
118 if key.eq(SHOW_LOG_WINDOW) {
119 self.enabled = value.parse().unwrap_or(false)
120 }
121 }
122 fn draw(&mut self, token: &MainThreadToken) {
124 Ui::window_begin("Log", &mut self.window_pose, Some(Vec2::new(self.x_len, 0.0) * CM), None, None);
125 self.draw_logs(token);
126 Ui::hseparator();
127 Ui::window_end();
128 }
129
130 fn draw_logs(&mut self, token: &MainThreadToken) {
131 let text_size = Vec2::new(Ui::get_layout_remaining().x, 0.024);
132 let items = self.log_log.lock().unwrap();
133
134 Ui::layout_push_cut(UiCut::Top, text_size.y * self.y_len, false);
135 Ui::layout_push_cut(UiCut::Right, Ui::get_line_height() * 0.6, false);
136
137 if self.items_size < items.len() {
138 self.items_size = items.len();
139 self.log_index = items.len() as f32;
140
141 }
145 if let Some(pos) =
146 Ui::vslider("scroll", &mut self.log_index, 0.0, items.len() as f32, Some(1.0), None, None, None)
147 {
148 self.log_index = f32::max(f32::min(pos, items.len() as f32 - 1.0), 0.0);
149 }
150
151 Ui::layout_pop();
152
153 let start = Ui::get_layout_at();
154 Ui::layout_reserve(Vec2::new(text_size.x, text_size.y * self.y_len), true, 0.0);
155
156 let mut index = (self.log_index - self.y_len) as i32;
157 let mut last_item_printed = self.log_index as i32;
158 if index < 0 {
159 index = 0;
160 last_item_printed = self.y_len as i32;
161 }
162 for i in index..last_item_printed {
163 if let Some(item) = items.get(i as usize) {
164 let ts = match item.level {
165 LogLevel::Diagnostic => self.style_diag,
166 LogLevel::Inform => self.style_info,
167 LogLevel::Warning => self.style_warn,
168 LogLevel::Error => self.style_err,
169 _ => self.style_info,
170 };
171
172 let y = (i - index) as f32 * -text_size.y;
173 Text::add_in(
174 token,
175 item.text.trim(),
176 Matrix::t(start + Vec3::new(0.0, y, -0.004)),
177 text_size,
178 TextFit::Clip | TextFit::Wrap,
179 Some(ts),
180 None,
181 Some(Pivot::TopLeft),
182 Some(Align::CenterLeft),
183 None,
184 None,
185 None,
186 );
187
188 if item.count > 1 {
189 let at = Vec3::new(start.x - text_size.x, start.y + y, start.z - 0.014);
190 Text::add_in(
191 token,
192 item.count.to_string(),
193 Matrix::t(at),
194 Vec2::new(text_size.x + 0.22, text_size.y),
195 TextFit::Clip,
196 Some(self.style_info),
197 None,
198 Some(Pivot::TopLeft),
199 Some(Align::CenterLeft),
200 None,
201 None,
202 None,
203 );
204 }
205 }
206 }
207 Ui::layout_pop();
208 }
209}
210
211pub fn basic_log_fmt(
216 level: LogLevel,
217 log_text: &str,
218 line_len: usize,
219 mut items: std::sync::MutexGuard<'_, Vec<LogItem>>,
220) {
221 for line_text in log_text.lines() {
222 let subs = line_text.as_bytes().chunks(line_len);
223 for (pos, sub_line) in subs.enumerate() {
224 if let Ok(mut sub_string) = String::from_utf8(sub_line.to_vec()) {
225 if pos > 0 {
226 sub_string.insert_str(0, "»»»»");
227 }
228 if let Some(item) = items.last_mut() {
229 if item.text == sub_string {
230 item.count += 1;
231 continue;
232 }
233 }
234
235 items.push(LogItem { level, text: sub_string.to_owned(), count: 1 });
236 };
237 }
238 }
239}