1use crate::geom::Coord;
9use crate::{ChildIndices, Id, Tile, TileExt, autoimpl};
10use std::{error::Error, fmt, path::Path};
11
12enum IdentifyContents<'a> {
13 Simple(&'a Id),
14 Wrapping(&'a dyn Tile),
15}
16
17pub struct IdentifyWidget<'a>(&'a str, IdentifyContents<'a>);
21impl<'a> IdentifyWidget<'a> {
22 pub fn simple(name: &'a str, id: &'a Id) -> Self {
24 IdentifyWidget(name, IdentifyContents::Simple(id))
25 }
26
27 pub fn wrapping(name: &'a str, inner: &'a dyn Tile) -> Self {
29 IdentifyWidget(name, IdentifyContents::Wrapping(inner))
30 }
31}
32impl<'a> fmt::Display for IdentifyWidget<'a> {
33 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
34 match self.1 {
35 IdentifyContents::Simple(id) => write!(f, "{}{}", self.0, id),
36 IdentifyContents::Wrapping(inner) => write!(f, "{}<{}>", self.0, inner.identify()),
37 }
38 }
39}
40
41struct Trail<'a> {
42 parent: Option<&'a Trail<'a>>,
43 trail: &'static str,
44}
45impl<'a> fmt::Display for Trail<'a> {
46 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
47 if let Some(p) = self.parent {
48 p.fmt(f)?;
49 }
50 write!(f, "{}", self.trail)
51 }
52}
53
54pub struct WidgetHierarchy<'a> {
58 widget: &'a dyn Tile,
59 filter: Option<Id>,
60 trail: Trail<'a>,
61 indent: usize,
62 have_next_sibling: bool,
63}
64impl<'a> WidgetHierarchy<'a> {
65 pub fn new(widget: &'a dyn Tile, filter: Option<Id>) -> Self {
66 WidgetHierarchy {
67 widget,
68 filter,
69 trail: Trail {
70 parent: None,
71 trail: "",
72 },
73 indent: 0,
74 have_next_sibling: false,
75 }
76 }
77}
78impl<'a> fmt::Display for WidgetHierarchy<'a> {
79 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
80 let len = 51 - 2 * self.indent;
81 let trail = &self.trail;
82 let (hook, trail_hook) = match self.indent >= 1 {
83 false => ("", ""),
84 true if self.have_next_sibling => ("├ ", "│ "),
85 true => ("└ ", " "),
86 };
87 let identify = format!("{}", self.widget.identify());
89 let r = self.widget.rect();
90 let Coord(x1, y1) = r.pos;
91 let Coord(x2, y2) = r.pos + r.size;
92 let xr = format!("x={x1}..{x2}");
93 let xrlen = xr.len().max(12);
94 write!(
95 f,
96 "\n{trail}{hook}{identify:<len$} {xr:<xrlen$} y={y1}..{y2}"
97 )?;
98
99 let indent = self.indent + 1;
100
101 if let Some(id) = self.filter.as_ref()
102 && let Some(index) = self.widget.find_child_index(id)
103 && let Some(widget) = self.widget.get_child(index)
104 {
105 return write!(f, "{}", WidgetHierarchy {
106 widget,
107 filter: self.filter.clone(),
108 trail: Trail {
109 parent: Some(trail),
110 trail: trail_hook,
111 },
112 indent,
113 have_next_sibling: false,
114 });
115 }
116
117 let mut iter = self.widget.children();
118 let mut next = iter.next();
119 while let Some(widget) = next {
120 next = iter.next();
121
122 if !widget.id_ref().is_valid() {
123 continue;
124 }
125
126 write!(f, "{}", WidgetHierarchy {
127 widget,
128 filter: None,
129 trail: Trail {
130 parent: Some(trail),
131 trail: trail_hook,
132 },
133 indent,
134 have_next_sibling: next.is_some(),
135 })?;
136 }
137 Ok(())
138 }
139}
140
141pub struct TryFormat<'a, T: ?Sized>(pub &'a T);
145
146#[cfg(not(feature = "spec"))]
147impl<'a, T: ?Sized> fmt::Debug for TryFormat<'a, T> {
148 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149 write!(f, "{{{}}}", std::any::type_name::<T>())
150 }
151}
152
153#[cfg(feature = "spec")]
154impl<'a, T: ?Sized> fmt::Debug for TryFormat<'a, T> {
155 default fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
156 write!(f, "{{{}}}", std::any::type_name::<T>())
157 }
158}
159
160#[cfg(feature = "spec")]
161impl<'a, T: fmt::Debug + ?Sized> fmt::Debug for TryFormat<'a, T> {
162 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163 write!(f, "{:?}", self.0)
164 }
165}
166
167pub fn nav_next(reverse: bool, from: Option<usize>, indices: ChildIndices) -> Option<usize> {
169 let range = indices.as_range();
170 if range.is_empty() {
171 return None;
172 }
173 let (first, last) = (range.start, range.end - 1);
174
175 if let Some(index) = from {
176 match reverse {
177 false if index < last => Some(index + 1),
178 true if first < index => Some(index - 1),
179 _ => None,
180 }
181 } else {
182 match reverse {
183 false => Some(first),
184 true => Some(last),
185 }
186 }
187}
188
189#[autoimpl(Default)]
193#[derive(Clone, Debug)]
194pub struct UndoStack<T> {
195 states: Vec<T>,
196 head: usize,
197}
198
199impl<T: PartialEq> UndoStack<T> {
200 #[inline]
202 pub const fn new() -> Self {
203 UndoStack {
204 states: vec![],
205 head: 0,
206 }
207 }
208
209 #[inline]
211 pub fn clear(&mut self) {
212 self.states.clear();
213 self.head = 0;
214 }
215
216 pub fn try_push(&mut self, state: T) {
222 if self.head > 0 && self.states.get(self.head - 1) == Some(&state) {
223 return;
224 }
225
226 self.states.truncate(self.head);
227 self.states.push(state);
228 self.head = self.states.len();
229 }
230
231 pub fn undo_or_redo(&mut self, redo: bool) -> Option<&T> {
233 let h = self.head;
234 let len = self.states.len();
235 let j = if redo && h < len {
236 h + 1
237 } else if !redo && h > 1 {
238 h - 1
239 } else {
240 return None;
241 };
242
243 self.head = j;
244 Some(&self.states[j - 1])
245 }
246}
247
248impl<K: Eq, S> UndoStack<(K, S)> {
249 pub fn clear_for_key(&mut self, key: K) {
253 let mut i = 0;
254 while i < self.states.len() {
255 if self.states[i].0 == key {
256 self.states.remove(i);
257 if i <= self.head {
258 self.head -= 1;
259 }
260 } else {
261 i += 1;
262 }
263 }
264 }
265}
266
267#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
269#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
270pub fn warn_about_error(msg: &str, mut error: &dyn Error) {
271 log::warn!("{msg}: {error}");
272 while let Some(source) = error.source() {
273 log::warn!("Source: {source}");
274 error = source;
275 }
276}
277
278#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
280#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
281pub fn warn_about_error_with_path(msg: &str, error: &dyn Error, path: &Path) {
282 warn_about_error(msg, error);
283 log::warn!("Path: {}", path.display());
284}