1use crate::geom::Coord;
9#[cfg(feature = "image")] use crate::window::Icon;
10use crate::{ChildIndices, Id, Tile, TileExt};
11use std::{error::Error, fmt, path::Path};
12
13enum IdentifyContents<'a> {
14 Simple(&'a Id),
15 Wrapping(&'a dyn Tile),
16}
17
18pub struct IdentifyWidget<'a>(&'a str, IdentifyContents<'a>);
22impl<'a> IdentifyWidget<'a> {
23 pub fn simple(name: &'a str, id: &'a Id) -> Self {
25 IdentifyWidget(name, IdentifyContents::Simple(id))
26 }
27
28 pub fn wrapping(name: &'a str, inner: &'a dyn Tile) -> Self {
30 IdentifyWidget(name, IdentifyContents::Wrapping(inner))
31 }
32}
33impl<'a> fmt::Display for IdentifyWidget<'a> {
34 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
35 match self.1 {
36 IdentifyContents::Simple(id) => write!(f, "{}{}", self.0, id),
37 IdentifyContents::Wrapping(inner) => write!(f, "{}<{}>", self.0, inner.identify()),
38 }
39 }
40}
41
42struct Trail<'a> {
43 parent: Option<&'a Trail<'a>>,
44 trail: &'static str,
45}
46impl<'a> fmt::Display for Trail<'a> {
47 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
48 if let Some(p) = self.parent {
49 p.fmt(f)?;
50 }
51 write!(f, "{}", self.trail)
52 }
53}
54
55pub struct WidgetHierarchy<'a> {
59 widget: &'a dyn Tile,
60 filter: Option<Id>,
61 trail: Trail<'a>,
62 indent: usize,
63 have_next_sibling: bool,
64}
65impl<'a> WidgetHierarchy<'a> {
66 pub fn new(widget: &'a dyn Tile, filter: Option<Id>) -> Self {
67 WidgetHierarchy {
68 widget,
69 filter,
70 trail: Trail {
71 parent: None,
72 trail: "",
73 },
74 indent: 0,
75 have_next_sibling: false,
76 }
77 }
78}
79impl<'a> fmt::Display for WidgetHierarchy<'a> {
80 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
81 let len = 51 - 2 * self.indent;
82 let trail = &self.trail;
83 let (hook, trail_hook) = match self.indent >= 1 {
84 false => ("", ""),
85 true if self.have_next_sibling => ("├ ", "│ "),
86 true => ("└ ", " "),
87 };
88 let identify = format!("{}", self.widget.identify());
90 let r = self.widget.rect();
91 let Coord(x1, y1) = r.pos;
92 let Coord(x2, y2) = r.pos + r.size;
93 let xr = format!("x={x1}..{x2}");
94 let xrlen = xr.len().max(12);
95 write!(
96 f,
97 "\n{trail}{hook}{identify:<len$} {xr:<xrlen$} y={y1}..{y2}"
98 )?;
99
100 let indent = self.indent + 1;
101
102 if let Some(id) = self.filter.as_ref()
103 && let Some(index) = self.widget.find_child_index(id)
104 && let Some(widget) = self.widget.get_child(index)
105 {
106 return write!(f, "{}", WidgetHierarchy {
107 widget,
108 filter: self.filter.clone(),
109 trail: Trail {
110 parent: Some(trail),
111 trail: trail_hook,
112 },
113 indent,
114 have_next_sibling: false,
115 });
116 }
117
118 let mut iter = self.widget.children();
119 let mut next = iter.next();
120 while let Some(widget) = next {
121 next = iter.next();
122
123 if !widget.id_ref().is_valid() {
124 continue;
125 }
126
127 write!(f, "{}", WidgetHierarchy {
128 widget,
129 filter: None,
130 trail: Trail {
131 parent: Some(trail),
132 trail: trail_hook,
133 },
134 indent,
135 have_next_sibling: next.is_some(),
136 })?;
137 }
138 Ok(())
139 }
140}
141
142pub struct TryFormat<'a, T: ?Sized>(pub &'a T);
146
147#[cfg(not(feature = "spec"))]
148impl<'a, T: ?Sized> fmt::Debug for TryFormat<'a, T> {
149 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150 write!(f, "{{{}}}", std::any::type_name::<T>())
151 }
152}
153
154#[cfg(feature = "spec")]
155impl<'a, T: ?Sized> fmt::Debug for TryFormat<'a, T> {
156 default fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157 write!(f, "{{{}}}", std::any::type_name::<T>())
158 }
159}
160
161#[cfg(feature = "spec")]
162impl<'a, T: fmt::Debug + ?Sized> fmt::Debug for TryFormat<'a, T> {
163 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164 write!(f, "{:?}", self.0)
165 }
166}
167
168pub fn nav_next(reverse: bool, from: Option<usize>, indices: ChildIndices) -> Option<usize> {
170 let range = indices.as_range();
171 if range.is_empty() {
172 return None;
173 }
174 let (first, last) = (range.start, range.end - 1);
175
176 if let Some(index) = from {
177 match reverse {
178 false if index < last => Some(index + 1),
179 true if first < index => Some(index - 1),
180 _ => None,
181 }
182 } else {
183 match reverse {
184 false => Some(first),
185 true => Some(last),
186 }
187 }
188}
189
190#[cfg(feature = "image")]
192pub fn load_icon_from_path<P: AsRef<std::path::Path>>(path: P) -> Result<Icon, Box<dyn Error>> {
193 let im = image::ImageReader::open(path)?
196 .with_guessed_format()?
197 .decode()?
198 .into_rgba8();
199 let (w, h) = im.dimensions();
200 Ok(Icon::from_rgba(im.into_vec(), w, h)?)
201}
202
203#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
205#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
206pub fn warn_about_error(msg: &str, mut error: &dyn Error) {
207 log::warn!("{msg}: {error}");
208 while let Some(source) = error.source() {
209 log::warn!("Source: {source}");
210 error = source;
211 }
212}
213
214#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
216#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
217pub fn warn_about_error_with_path(msg: &str, error: &dyn Error, path: &Path) {
218 warn_about_error(msg, error);
219 log::warn!("Path: {}", path.display());
220}