Count: measured messages and listing, groups, Current area, boundary checks, fills and solids recoloured

- COUNTCOLOR / COUNTERRORCOLOR / COUNTSERVICE reject any other value with
  the range message; COUNTTABLE ? lists in the reference order, padded,
  22 lines a page with Press ENTER to continue:, then the totals.
- Target picks report N found / N found, M total; several objects (or one
  that is not a block reference) count their copies, shown as Geometries
  in the palette.
- Current area is the area in use (kept aside by Entire model space),
  otherwise all of model space; an invalid Object boundary ends with the
  reference message.
- Count colours reach solids and fills; the palette list and the count
  are recomputed only when the drawing changes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-10-05 11:36:24 +03:00
1 parent d830e40bfa
commit 0a26709d13
31 files changed
+214 -60

No files matched your search

Generated
+1 -1
View File
@@ -4206,7 +4206,7 @@ dependencies = [
[[package]]
name = "opencadcodec"
version = "0.5.5"
source = "git+https://github.com/ramox81/cadcodec.git?rev=48b86d0#48b86d08c895f07b53753888363d1d9a6b282305"
source = "git+https://github.com/ramox81/cadcodec.git?rev=178b596#178b596ba1606ce6d4ed719cd48551750a0f32a5"
dependencies = [
"ahash 0.8.12",
"anyhow",
+1 -1
View File
@@ -91,7 +91,7 @@ block2 = "0.5"
objc2-foundation = { version = "0.2", features = ["NSArray", "NSURL", "NSString", "NSObject", "NSNotification", "NSThread", "NSEnumerator"] }
[patch."https://github.com/HakanSeven12/opencadcodec.git"]
opencadcodec = { git = "https://github.com/ramox81/cadcodec.git", rev = "48b86d0" }
opencadcodec = { git = "https://github.com/ramox81/cadcodec.git", rev = "178b596" }
[patch.crates-io]
iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" }
+1
View File
@@ -5955,3 +5955,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5943,3 +5943,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5940,3 +5940,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5941,3 +5941,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5943,3 +5943,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5944,3 +5944,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5943,3 +5943,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5940,3 +5940,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5942,3 +5942,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5931,3 +5931,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5942,3 +5942,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5942,3 +5942,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5931,3 +5931,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5942,3 +5942,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5941,3 +5941,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5942,3 +5942,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5943,3 +5943,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5944,3 +5944,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5866,3 +5866,4 @@ count-feature =
.toolbar-count = Sayım: __ocs_fmt_0__
.overlap = Üst üste binen nesne
.show-instances = Örnekleri göster
.geometries = Geometriler: __ocs_fmt_0__
+1
View File
@@ -5926,3 +5926,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+1
View File
@@ -5938,3 +5938,4 @@ count-feature =
.toolbar-count = Count: __ocs_fmt_0__
.overlap = Overlapping object
.show-instances = Show Count Instances
.geometries = Geometries: __ocs_fmt_0__
+31 -17
View File
@@ -216,7 +216,7 @@ impl OpenCADStudio {
self.persist_settings_if_changed();
self.apply_count_display(i);
}
Ok(_) => {
_ => {
self.command_line.push_error(&if max == 1 {
"Requires 0 or 1 only.".to_string()
} else {
@@ -224,10 +224,6 @@ impl OpenCADStudio {
});
ask(self);
}
Err(_) => {
self.command_line.push_error("Requires an integer value.");
ask(self);
}
}
}
@@ -249,6 +245,10 @@ impl OpenCADStudio {
/// Recolour the drawing for count mode: counted references, duplicates,
/// the rest faded (nothing changes without a target).
pub(in crate::app) fn apply_count_display(&mut self, i: usize) {
let computed = self.tabs[i].count.as_ref().map(|m| m.compute(&self.tabs[i].scene.document));
if let (Some(mode), Some(c)) = (self.tabs[i].count.as_mut(), computed) {
mode.cache = Some(c);
}
let display = self.tabs[i].count.as_ref().filter(|m| m.target.is_some()).map(|m| {
let r = m.result(&self.tabs[i].scene.document);
crate::scene::CountDisplay {
@@ -278,6 +278,7 @@ impl OpenCADStudio {
pub(in crate::app) fn close_count(&mut self, i: usize) {
if let Some(mode) = self.tabs[i].count.take() {
self.drop_count_boundary(i, mode.boundary);
self.drop_count_boundary(i, mode.remembered.and_then(|(_, b)| b));
}
self.tabs[i].scene.set_count_display(None);
}
@@ -310,10 +311,13 @@ impl OpenCADStudio {
let mode = self.tabs[i].count.as_ref();
Some((mode.and_then(|m| m.area.clone()), mode.and_then(|m| m.boundary)))
}
"C" => {
let (x0, y0, x1, y1) = self.display_plot_window()?;
Some((Some(vec![[x0, y0], [x1, y0], [x1, y1], [x0, y1]]), None))
}
// Current area: the count area in use (also one Entire model space
// set aside), else all of model space.
"C" => Some(match self.tabs[i].count.as_ref() {
Some(m) if m.area.is_some() => (m.area.clone(), m.boundary),
Some(m) => m.remembered.clone().map_or((None, None), |(a, b)| (Some(a), b)),
None => (None, None),
}),
"R" => {
let p = points();
let (a, b) = (p.first()?, p.get(1)?);
@@ -404,11 +408,18 @@ impl OpenCADStudio {
};
// The picked targets do not stay selected.
self.tabs[i].scene.deselect_all();
let old = self.tabs[i].count.take();
if let Some(old) = old.as_ref().filter(|o| o.boundary != boundary) {
self.drop_count_boundary(i, old.boundary);
// Entire model space counts without the area but keeps it for a later
// Current area; a new area replaces it.
let mut remembered = None;
if let Some(old) = self.tabs[i].count.take() {
let old_area = old.area.map(|a| (a, old.boundary)).or(old.remembered);
if area.is_none() {
remembered = old_area;
} else if let Some((_, b)) = old_area.filter(|(_, b)| *b != boundary) {
self.drop_count_boundary(i, b);
}
}
let mode = CountMode { area, boundary, target, cursor: None, epoch: 0 };
let mode = CountMode { area, boundary, target, remembered, ..CountMode::default() };
if let Some(name) = mode.target_name() {
let n = mode.count(&self.tabs[i].scene.document);
self.command_line.push_output(&format!("{name} ...... {n}"));
@@ -566,13 +577,16 @@ impl OpenCADStudio {
self.tabs[i].dirty = true;
}
/// Zoom to a reference (← / →, an error of the report).
/// Zoom to a counted object (← / →, an error of the report).
fn zoom_to_insert(&mut self, i: usize, h: Handle) {
let doc = &self.tabs[i].scene.document;
let Some(EntityType::Insert(ins)) = doc.get_entity(h) else { return };
let Some(corners) = codec::count::insert_corners(doc, ins, 0) else { return };
let Some(entity) = doc.get_entity(h) else { return };
let outline = codec::count::entity_outline(doc, entity);
if outline.iter().all(|part| part.is_empty()) {
return;
}
let (mut lo, mut hi) = (glam::Vec3::splat(f32::MAX), glam::Vec3::splat(f32::MIN));
for c in corners {
for c in outline.into_iter().flatten() {
let p = glam::Vec3::new(c.x as f32, c.y as f32, c.z as f32);
lo = lo.min(p);
hi = hi.max(p);
+1 -1
View File
@@ -5544,7 +5544,7 @@ impl OpenCADStudio {
/// Current visible rectangle in the active space. The result is deliberately
/// not clamped to the paper sheet: Display and Window may include the grey
/// canvas outside the sheet, matching Model-space plotting.
pub(in crate::app) fn display_plot_window(&self) -> Option<(f64, f64, f64, f64)> {
fn display_plot_window(&self) -> Option<(f64, f64, f64, f64)> {
let scene = &self.tabs[self.active_tab].scene;
let (canvas_w, canvas_h) = scene.selection.borrow().vp_size;
let viewport = if scene.current_layout == "Model" {
+1
View File
@@ -3035,6 +3035,7 @@ impl OpenCADStudio {
&self.count_palette,
tab.count.as_ref(),
&tab.scene.document,
tab.scene.geometry_epoch,
width,
auto_collapse,
),
+1
View File
@@ -5545,6 +5545,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"Count: {}" => Some(("count-feature", "toolbar-count")),
"Overlapping object" => Some(("count-feature", "overlap")),
"Show Count Instances" => Some(("count-feature", "show-instances")),
"Geometries: {}" => Some(("count-feature", "geometries")),
_ => None,
}
}
+69 -18
View File
@@ -144,8 +144,16 @@ impl CadCommand for CountCommand {
}
fn on_selection_complete(&mut self, handles: Vec<Handle>) -> CmdResult {
// The selection arrives whole after every pick: `1 found`, then
// `1 found, 2 total`.
let found = handles.iter().filter(|h| !self.targets.contains(h)).count();
let first = self.targets.is_empty();
self.targets = handles;
CmdResult::ReportMeasurement(format!("{} found", self.targets.len()))
CmdResult::ReportMeasurement(if first {
format!("{found} found")
} else {
format!("{found} found, {} total", self.targets.len())
})
}
fn needs_entity_pick(&self) -> bool {
@@ -162,11 +170,13 @@ impl CadCommand for CountCommand {
fn on_entity_pick(&mut self, handle: Handle, _pt: DVec3) -> CmdResult {
match self.picked.take() {
Some(EntityType::LwPolyline(pl)) if pl.is_closed && pl.vertices.len() >= 3 => {
Some(EntityType::LwPolyline(pl)) if codec::count::valid_boundary(&pl) => {
self.area_done(format!("O {:X}", handle.value()))
}
// Anything else: the prompt again.
_ => CmdResult::NeedPoint,
_ => CmdResult::CancelWithMessage(
"Invalid count area boundary object. Select a closed polyline consisting of line segments and does not intersect itself."
.into(),
),
}
}
@@ -244,6 +254,8 @@ pub struct CountTableCommand {
blocks: Vec<String>,
others: [usize; 3],
names: Vec<String>,
/// Listing lines still to show after `Press ENTER to continue:`.
pending: Vec<String>,
}
#[derive(Clone, Copy, PartialEq, Eq)]
@@ -251,38 +263,75 @@ enum TableStep {
Names,
List,
Point,
Page,
}
impl CountTableCommand {
pub fn new(blocks: Vec<String>, others: [usize; 3]) -> Self {
Self { step: TableStep::Names, blocks, others, names: Vec::new() }
Self { step: TableStep::Names, blocks, others, names: Vec::new(), pending: Vec::new() }
}
/// The palette's Create Table: the blocks are chosen, only the point is asked.
pub fn placing(names: Vec<String>) -> Self {
Self { step: TableStep::Point, blocks: Vec::new(), others: [0; 3], names }
Self { step: TableStep::Point, blocks: Vec::new(), others: [0; 3], names, pending: Vec::new() }
}
/// `?`: the defined blocks matching the pattern and the summary.
fn listing(&self, pattern: &str) -> String {
/// `?`: the defined blocks matching the pattern, paged by 22 lines
/// (`Press ENTER to continue:` in between), then the summary.
fn listing(&mut self, pattern: &str) -> CmdResult {
let pattern = if pattern.trim().is_empty() { "*" } else { pattern.trim() };
let mut out = String::from("Defined blocks.");
for name in &self.blocks {
if pattern.split(',').any(|p| crate::io::xref_model::wildcard_match(name, p.trim())) {
out.push_str(&format!("\n \"{name}\""));
}
let mut names: Vec<&String> = self
.blocks
.iter()
.filter(|name| pattern.split(',').any(|p| crate::io::xref_model::wildcard_match(name, p.trim())))
.collect();
names.sort_by_key(|name| list_order(name));
self.pending = std::iter::once(String::new())
.chain(std::iter::once("Defined blocks.".to_string()))
.chain(names.iter().map(|name| format!(" {:<31}", format!("\"{name}\""))))
.collect();
self.next_page()
}
fn next_page(&mut self) -> CmdResult {
let page: Vec<String> = self.pending.drain(..self.pending.len().min(22)).collect();
if !self.pending.is_empty() {
self.step = TableStep::Page;
return CmdResult::ReportMeasurement(page.join("
"));
}
out.push_str(&format!(
"\n\nUser External Dependent Unnamed\nBlocks References Blocks Blocks\n{:>5}{:>11}{:>13}{:>12}",
CmdResult::Measurement(format!(
"{}
User External Dependent Unnamed
Blocks References Blocks Blocks
{:>5}{:>10}{:>12}{:>12}
",
page.join("
"),
self.blocks.len(),
self.others[0],
self.others[1],
self.others[2]
));
out
))
}
}
/// Block listing order: `_` first, then `-`, space and other punctuation,
/// digits, and letters without case.
fn list_order(name: &str) -> Vec<(u32, u32)> {
name.chars()
.map(|c| match c {
'_' => (0, 0),
'-' => (1, 0),
' ' => (2, 0),
'0'..='9' => (4, c as u32),
c if c.is_alphabetic() => (5, c.to_lowercase().next().unwrap_or(c) as u32),
c => (3, c as u32),
})
.collect()
}
impl CadCommand for CountTableCommand {
fn name(&self) -> &'static str {
"COUNTTABLE"
@@ -293,6 +342,7 @@ impl CadCommand for CountTableCommand {
TableStep::Names => "Enter block name(s) to include or [?] <all blocks>:".into(),
TableStep::List => "Enter block(s) to list <*>:".into(),
TableStep::Point => "Specify insertion point:".into(),
TableStep::Page => "Press ENTER to continue:".into(),
}
}
@@ -327,7 +377,8 @@ impl CadCommand for CountTableCommand {
self.step = TableStep::Point;
CmdResult::NeedPoint
}
TableStep::List => CmdResult::Measurement(self.listing(text)),
TableStep::List => self.listing(text),
TableStep::Page => self.next_page(),
TableStep::Point => return None,
})
}
+2
View File
@@ -1451,6 +1451,7 @@ impl Scene {
}
}
}
m.color = self.count_color(handle, m.color, self.current_bg());
if tint_selected && self.selected.contains(&handle) {
m.color = [0.15, 0.55, 1.00, m.color[3]];
}
@@ -1752,6 +1753,7 @@ impl Scene {
)
});
}
model.color = self.count_color(context.root_handle, model.color, hatch_bg);
if tint_selected && self.selected.contains(&context.root_handle) {
model.color = [0.15, 0.55, 1.00, model.color[3]];
}
+31 -2
View File
@@ -4007,6 +4007,9 @@ impl Scene {
);
}
}
if self.meshes.contains_key(&h) {
material.diffuse = self.count_color(h, material.diffuse, bg);
}
(h, material)
})
})
@@ -4108,9 +4111,28 @@ impl Scene {
return;
}
self.count_display = display;
self.hatch_cache.borrow_mut().clear();
self.recolor_meshes();
self.bump_geometry_no_blocks();
}
/// The colour count mode gives an object drawn as part of `handle` (a
/// top-level entity): the count colours, else faded; unchanged outside
/// count mode.
pub(crate) fn count_color(&self, handle: Handle, color: [f32; 4], bg: [f32; 4]) -> [f32; 4] {
let Some(count) = &self.count_display else {
return color;
};
let [r, g, b, _] = if count.errors.contains(&handle) {
count.error_color
} else if count.counted.contains(&handle) {
count.color
} else {
return crate::scene::cache::block_cache::fade_toward_bg(color, bg);
};
[r, g, b, color[3]]
}
/// Fade the colours of wires that belong to entities outside the REFEDIT
/// keep set (no-op when not editing). The geometry is untouched, so
/// hit-testing still works on faded entities. In count mode the counted
@@ -4118,8 +4140,7 @@ impl Scene {
fn apply_refedit_fade(&self, wires: &mut [WireModel], bg: [f32; 4]) {
if let Some(count) = &self.count_display {
for w in wires.iter_mut() {
let h = Self::handle_from_wire_name(&w.name);
w.color = match h {
w.color = match Self::handle_from_wire_name(&w.name) {
Some(h) if count.errors.contains(&h) => count.error_color,
Some(h) if count.counted.contains(&h) => count.color,
_ => crate::scene::cache::block_cache::fade_toward_bg(w.color, bg),
@@ -8745,6 +8766,14 @@ impl Scene {
self.material_base_dir.as_deref(),
);
}
if self.count_display.is_some() {
let own = transformed.instance_color.or(set.display_color()).unwrap_or([1.0; 4]);
let color = self.count_color(context.root_handle, own, self.current_bg());
transformed.instance_color = Some(color);
if let Some(material) = transformed.material.as_mut() {
material.diffuse = color;
}
}
transformed.instance_handle = Some(context.root_handle);
out.push(transformed);
},
+55 -20
View File
@@ -33,7 +33,8 @@ pub enum CountTarget {
matching: [bool; 3],
picked: bool,
},
/// Several picked objects counted as one group.
/// Picked objects (several, or one that is not a reference): the copies
/// of the group are counted.
Group(Vec<Handle>),
}
@@ -50,6 +51,10 @@ pub struct CountMode {
pub cursor: Option<usize>,
/// The scene geometry epoch the colouring was computed at.
pub epoch: u64,
/// The area Entire model space set aside, for a later Current area.
pub remembered: Option<(Vec<[f64; 2]>, Option<(Handle, bool)>)>,
/// The result and count at `epoch` (recomputed when the drawing changes).
pub cache: Option<(CountResult, usize)>,
}
/// A count mode's result: the counted references and the overlapping
@@ -80,13 +85,35 @@ impl CountMode {
}
pub fn result(&self, doc: &CadDocument) -> CountResult {
match &self.cache {
Some((result, _)) => result.clone(),
None => self.compute(doc).0,
}
}
/// What the count shows: the counted references, or the copies of a group.
pub fn count(&self, doc: &CadDocument) -> usize {
match &self.cache {
Some((_, n)) => *n,
None => self.compute(doc).1,
}
}
/// The result and the count, worked out from the drawing.
pub fn compute(&self, doc: &CadDocument) -> (CountResult, usize) {
if let Some(CountTarget::Group(handles)) = &self.target {
let groups = codec::count::group_matches(doc, handles, self.area.as_deref());
let result = CountResult { counted: groups.concat(), errors: Vec::new(), overlapped: Vec::new() };
return (result, groups.len());
}
let result = self.block_result(doc);
let n = result.counted.len();
(result, n)
}
fn block_result(&self, doc: &CadDocument) -> CountResult {
match &self.target {
None => CountResult::default(),
Some(CountTarget::Group(handles)) => CountResult {
counted: handles.iter().copied().filter(|h| doc.get_entity(*h).is_some()).collect(),
errors: Vec::new(),
overlapped: Vec::new(),
},
None | Some(CountTarget::Group(_)) => CountResult::default(),
Some(CountTarget::Block { name, .. }) => {
let instances = self.instances(doc);
let key = self.key(&instances);
@@ -100,15 +127,6 @@ impl CountMode {
}
}
/// What the count shows: the counted references, or 1 for a group whose
/// objects all exist.
pub fn count(&self, doc: &CadDocument) -> usize {
match &self.target {
Some(CountTarget::Group(handles)) => usize::from(handles.iter().all(|h| doc.get_entity(*h).is_some())),
_ => self.result(doc).counted.len(),
}
}
/// The name the toolbar and the command line show for the target.
pub fn target_name(&self) -> Option<String> {
match &self.target {
@@ -137,6 +155,8 @@ pub struct CountPalette {
pub color: i16,
pub error_color: i16,
pub service: bool,
/// The list's references for (document, geometry epoch, area).
pub list_cache: std::cell::RefCell<Option<(usize, u64, Option<Vec<[f64; 2]>>, std::rc::Rc<Vec<BlockInstance>>)>>,
}
impl Default for CountPalette {
@@ -153,6 +173,7 @@ impl Default for CountPalette {
color: 3,
error_color: 1,
service: true,
list_cache: Default::default(),
}
}
}
@@ -434,7 +455,12 @@ fn mode_view<'a>(palette: &'a CountPalette, mode: &'a CountMode, doc: &CadDocume
let mut details = column![button(
row![
crate::ui::icons::themed_success(CHECK_ICON, 12.0),
text(format!("{name}: {}", mode.count(doc))).size(12).width(Fill),
text(match mode.target {
Some(CountTarget::Group(_)) => crate::tf!("Geometries: {}", mode.count(doc)).into_owned(),
_ => format!("{name}: {}", mode.count(doc)),
})
.size(12)
.width(Fill),
]
.spacing(6)
.align_y(iced::Center),
@@ -488,16 +514,25 @@ pub fn view<'a>(
palette: &'a CountPalette,
mode: Option<&'a CountMode>,
doc: &'a CadDocument,
epoch: u64,
width: f32,
auto_collapse: bool,
) -> Element<'a, Message> {
let title_bar = crate::ui::dock::title_bar(PanelId::Count, crate::t!("Count palette").into_owned(), auto_collapse);
// ponytail: the list is recounted on every view; cache by geometry epoch
// if very large drawings make the palette slow.
let body = match mode {
Some(mode) if mode.target.is_some() => mode_view(palette, mode, doc),
_ => {
let instances = codec::count::block_instances(doc, mode.and_then(|m| m.area.as_deref()));
let area = mode.and_then(|m| m.area.clone());
let key = (doc as *const CadDocument as usize, epoch);
let mut cache = palette.list_cache.borrow_mut();
let instances = match cache.as_ref() {
Some((d, e, a, list)) if (*d, *e) == key && *a == area => list.clone(),
_ => {
let list = std::rc::Rc::new(codec::count::block_instances(doc, area.as_deref()));
*cache = Some((key.0, key.1, area, list.clone()));
list
}
};
list_view(palette, &instances)
}
};