mirror of
https://github.com/storytold/craft-libs.git
synced 2026-10-31 15:49:54 +00:00
docking: paint panel icons in code with DockContent::paint_icon
This commit is contained in:
1 parent
f4f9e5b206
commit
d91e5f6aea
4 files changed
+63
-7
No files matched your search
@@ -324,3 +324,46 @@ fn hit_rects_cover_every_gesture_source() {
|
||||
assert!(opened || !out.actions.is_empty(), "{hit:?} at {rect:?} does nothing");
|
||||
}
|
||||
}
|
||||
|
||||
/// Paints panel 1's icon as a dot in code; panel 2 has neither a painted icon nor an image.
|
||||
struct Painted;
|
||||
|
||||
impl DockContent<u32> for Painted {
|
||||
fn body(&mut self, _: &mut egui::Ui, _: &u32) {}
|
||||
fn logical_label(&self, panel: &u32) -> String {
|
||||
format!("Panel {panel}")
|
||||
}
|
||||
fn paint_icon(&mut self, painter: &egui::Painter, rect: Rect, panel: &u32, color: egui::Color32) -> bool {
|
||||
if *panel != 1 {
|
||||
return false;
|
||||
}
|
||||
painter.circle_filled(rect.center(), 3.0, color);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_icon_painted_in_code_replaces_the_initial_and_others_fall_back() {
|
||||
let d = DockDriver::new(docked(&[&[1, 2]], true), Painted);
|
||||
let (one, two) = (d.hit(&Hit::Icon(1)).unwrap(), d.hit(&Hit::Icon(2)).unwrap());
|
||||
let dots: Vec<Pos2> = d
|
||||
.shapes()
|
||||
.iter()
|
||||
.filter_map(|clipped| match &clipped.shape {
|
||||
egui::Shape::Circle(circle) if circle.radius == 3.0 => Some(circle.center),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(dots.len(), 1, "{dots:?}");
|
||||
assert!(one.contains(dots[0]) && !two.contains(dots[0]), "the dot sits in panel 1's cell: {dots:?}");
|
||||
let initials: Vec<Pos2> = d
|
||||
.shapes()
|
||||
.iter()
|
||||
.filter_map(|clipped| match &clipped.shape {
|
||||
egui::Shape::Text(text) if text.galley.text() == "P" => Some(text.pos),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(initials.len(), 1, "only panel 2 draws its initial: {initials:?}");
|
||||
assert!(two.contains(initials[0]));
|
||||
}
|
||||
@@ -230,12 +230,14 @@ impl<P: Clone + Eq + Hash + Debug + Send + Sync + 'static> Renderer<'_, P> {
|
||||
let inset = ((self.style.iconic_width - size) * 0.5).max(2.0);
|
||||
let icon = Rect::from_min_size(pos2(cell.left() + inset, cell.center().y - size * 0.5), vec2(size, size));
|
||||
let color = if current { self.style.text } else { self.style.inactive_text };
|
||||
if let Some(source) = self.content.icon(panel) {
|
||||
egui::Image::new(source).tint(color).paint_at(ui, icon.intersect(cell));
|
||||
} else {
|
||||
let initial: String = label.chars().next().into_iter().flat_map(char::to_uppercase).collect();
|
||||
painter.rect_stroke(icon, 3.0, Stroke::new(1.0, color), StrokeKind::Inside);
|
||||
painter.text(icon.center(), egui::Align2::CENTER_CENTER, initial, self.style.font.clone(), color);
|
||||
if !self.content.paint_icon(&painter, icon, panel, color) {
|
||||
if let Some(source) = self.content.icon(panel) {
|
||||
egui::Image::new(source).tint(color).paint_at(ui, icon.intersect(cell));
|
||||
} else {
|
||||
let initial: String = label.chars().next().into_iter().flat_map(char::to_uppercase).collect();
|
||||
painter.rect_stroke(icon, 3.0, Stroke::new(1.0, color), StrokeKind::Inside);
|
||||
painter.text(icon.center(), egui::Align2::CENTER_CENTER, initial, self.style.font.clone(), color);
|
||||
}
|
||||
}
|
||||
if labels {
|
||||
let text = self.content.label(ui, panel, &label, &self.style.font, color);
|
||||
|
||||
@@ -371,9 +371,16 @@ pub trait DockContent<P> {
|
||||
fn floating_size(&self, _panel: &P) -> Option<Vec2> {
|
||||
None
|
||||
}
|
||||
/// An image for the panel's icon, tinted with the icon color. [`Self::paint_icon`] goes first.
|
||||
fn icon(&self, _panel: &P) -> Option<egui::ImageSource<'static>> {
|
||||
None
|
||||
}
|
||||
/// Paint the panel's icon in code into `rect` with `color`, and return true. False draws
|
||||
/// [`Self::icon`], or the label's initial when that is `None`. `painter` is clipped to the
|
||||
/// icon's cell.
|
||||
fn paint_icon(&mut self, _painter: &egui::Painter, _rect: Rect, _panel: &P, _color: Color32) -> bool {
|
||||
false
|
||||
}
|
||||
/// Whether the panel may move to a native OS window (feature `native-windows`).
|
||||
fn allow_native(&self, _panel: &P) -> bool {
|
||||
false
|
||||
@@ -455,6 +462,9 @@ impl<P, C: DockContent<P> + ?Sized> DockContent<P> for Overrides<'_, P, C> {
|
||||
fn icon(&self, panel: &P) -> Option<egui::ImageSource<'static>> {
|
||||
self.inner.icon(panel)
|
||||
}
|
||||
fn paint_icon(&mut self, painter: &egui::Painter, rect: Rect, panel: &P, color: Color32) -> bool {
|
||||
self.inner.paint_icon(painter, rect, panel, color)
|
||||
}
|
||||
fn allow_native(&self, panel: &P) -> bool {
|
||||
self.inner.allow_native(panel)
|
||||
}
|
||||
|
||||
+2
-1
@@ -104,7 +104,8 @@ its own command path, so pointer, keyboard and control commands share one path.
|
||||
|
||||
`DockContent<P>` is the app's adapter. It draws each panel body and gives its label, icon,
|
||||
permissions, size limits, preferred floating size, panel menu and group header. `bare` hides the
|
||||
tab strip of a lone panel. Panel bodies keep a stable egui id when they move, so widget memory
|
||||
tab strip of a lone panel. An icon is an image (`icon`), or drawn in code by `paint_icon`, which
|
||||
gets a painter, the icon rect and the tint color and returns true when it painted. Panel bodies keep a stable egui id when they move, so widget memory
|
||||
survives.
|
||||
|
||||
`DockOutput` also has:
|
||||
|
||||
Reference in new issue
Block a user