feat(locale): localize full interface

This commit is contained in:
Hakan Seven 2026-08-02 22:09:25 +03:00
commit 3e6693b93f
205 changed files with 32367 additions and 3808 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -172,7 +172,7 @@ impl OpenCADStudio {
tasks.push(self.apply_cmd_result(CmdResult::CancelForSpaceChange)); tasks.push(self.apply_cmd_result(CmdResult::CancelForSpaceChange));
} else if message_pending { } else if message_pending {
self.command_line self.command_line
.push_info("Command cancelled because the active drawing space changed."); .push_info(crate::t!("Command cancelled because the active drawing space changed.").as_ref());
} }
cancellation_reported = true; cancellation_reported = true;
} }
@ -186,7 +186,7 @@ impl OpenCADStudio {
} }
if !cancellation_reported && (grip_cancelled || suspended_cancelled || editor_cancelled) { if !cancellation_reported && (grip_cancelled || suspended_cancelled || editor_cancelled) {
self.command_line self.command_line
.push_info("Command cancelled because the active drawing space changed."); .push_info(crate::t!("Command cancelled because the active drawing space changed.").as_ref());
} }
self.command_line.input.clear(); self.command_line.input.clear();
@ -210,7 +210,7 @@ impl OpenCADStudio {
.is_some_and(|command| command.name() != "LIMITS") .is_some_and(|command| command.name() != "LIMITS")
&& self.tabs[i].scene.drawing_limit_check_enabled(); && self.tabs[i].scene.drawing_limit_check_enabled();
if checks_limits && !self.tabs[i].scene.point_inside_drawing_limits(point) { if checks_limits && !self.tabs[i].scene.point_inside_drawing_limits(point) {
self.command_line.push_error("Outside limits."); self.command_line.push_error(crate::t!("Outside limits.").as_ref());
return false; return false;
} }
true true
@ -389,7 +389,7 @@ impl OpenCADStudio {
self.tabs[i].scene.select_entity(h, false); self.tabs[i].scene.select_entity(h, false);
} }
self.command_line self.command_line
.push_info(&format!("{count} object(s) added to selection.")); .push_info(crate::tf!("{count} object(s) added to selection.").as_ref());
self.refresh_properties(); self.refresh_properties();
let handles: Vec<Handle> = self.tabs[i] let handles: Vec<Handle> = self.tabs[i]
.scene .scene
@ -754,7 +754,7 @@ impl OpenCADStudio {
}); });
let Some((min_x, min_y, max_x, max_y)) = bounds else { let Some((min_x, min_y, max_x, max_y)) = bounds else {
self.command_line self.command_line
.push_error("MVIEW: the clipping boundary has no usable area."); .push_error(crate::t!("MVIEW: the clipping boundary has no usable area.").as_ref());
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
if let Some(pending) = pending { if let Some(pending) = pending {
self.commit_undo_delta(i, pending); self.commit_undo_delta(i, pending);
@ -763,7 +763,7 @@ impl OpenCADStudio {
}; };
if max_x - min_x < 1e-6 || max_y - min_y < 1e-6 { if max_x - min_x < 1e-6 || max_y - min_y < 1e-6 {
self.command_line self.command_line
.push_error("MVIEW: the clipping boundary has no usable area."); .push_error(crate::t!("MVIEW: the clipping boundary has no usable area.").as_ref());
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
if let Some(pending) = pending { if let Some(pending) = pending {
self.commit_undo_delta(i, pending); self.commit_undo_delta(i, pending);
@ -1051,7 +1051,7 @@ impl OpenCADStudio {
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
self.command_line self.command_line
.push_output(&format!("Block \"{name}\" created.")); .push_output(crate::tf!("Block \"{name}\" created.").as_ref());
self.refresh_properties(); self.refresh_properties();
} }
Err(err) => { Err(err) => {
@ -1098,7 +1098,7 @@ impl OpenCADStudio {
self.restore_pre_cmd_tangent(); self.restore_pre_cmd_tangent();
let noun = if count == 1 { "copy" } else { "copies" }; let noun = if count == 1 { "copy" } else { "copies" };
self.command_line self.command_line
.push_output(&format!("{label}: {count} {noun} created.")); .push_output(crate::tf!("{label}: {count} {noun} created.").as_ref());
self.refresh_properties(); self.refresh_properties();
if let Some(pd) = pending { if let Some(pd) = pending {
self.commit_undo_delta(i, pd); self.commit_undo_delta(i, pd);
@ -1133,7 +1133,7 @@ impl OpenCADStudio {
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
if was_catchment { if was_catchment {
self.command_line self.command_line
.push_info("Catchment tagged successfully."); .push_info(crate::t!("Catchment tagged successfully.").as_ref());
} }
self.refresh_properties(); self.refresh_properties();
} }
@ -1171,7 +1171,7 @@ impl OpenCADStudio {
// Report honestly rather than claiming success while // Report honestly rather than claiming success while
// changing nothing. (#181 / DIM-020) // changing nothing. (#181 / DIM-020)
self.command_line self.command_line
.push_info("DIMBREAK: not yet implemented — nothing changed."); .push_info(crate::t!("DIMBREAK: not yet implemented — nothing changed.").as_ref());
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
return Task::none(); return Task::none();
@ -1181,7 +1181,7 @@ impl OpenCADStudio {
apply_dimspace(&mut self.tabs[i].scene, encoded); apply_dimspace(&mut self.tabs[i].scene, encoded);
} }
self.push_undo_snapshot(i, "DIMSPACE"); self.push_undo_snapshot(i, "DIMSPACE");
self.command_line.push_output("DIMSPACE Spacing adjusted."); self.command_line.push_output(crate::t!("DIMSPACE Spacing adjusted.").as_ref());
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
@ -1192,7 +1192,7 @@ impl OpenCADStudio {
// model (not yet present) to store and render the jog. // model (not yet present) to store and render the jog.
// Report honestly rather than faking success. (DIM-019) // Report honestly rather than faking success. (DIM-019)
self.command_line self.command_line
.push_info("DIMJOGLINE: not yet implemented — nothing changed."); .push_info(crate::t!("DIMJOGLINE: not yet implemented — nothing changed.").as_ref());
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
return Task::none(); return Task::none();
@ -1203,7 +1203,7 @@ impl OpenCADStudio {
} }
self.push_undo_snapshot(i, "MLEADERALIGN"); self.push_undo_snapshot(i, "MLEADERALIGN");
self.command_line self.command_line
.push_output("MLEADERALIGN Leaders aligned."); .push_output(crate::t!("MLEADERALIGN Leaders aligned.").as_ref());
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
@ -1215,7 +1215,7 @@ impl OpenCADStudio {
} }
self.push_undo_snapshot(i, "MLEADERCOLLECT"); self.push_undo_snapshot(i, "MLEADERCOLLECT");
self.command_line self.command_line
.push_output("MLEADERCOLLECT Leaders collected."); .push_output(crate::t!("MLEADERCOLLECT Leaders collected.").as_ref());
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
@ -1461,10 +1461,10 @@ impl OpenCADStudio {
self.invalidate_property_targets(i, &dest); self.invalidate_property_targets(i, &dest);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_info(&format!("Layer matched to \"{layer}\".")); .push_info(crate::tf!("Layer matched to \"{layer}\".").as_ref());
self.sync_ribbon_layers(); self.sync_ribbon_layers();
} else { } else {
self.command_line.push_error("Source object not found."); self.command_line.push_error(crate::t!("Source object not found.").as_ref());
} }
} }
CmdResult::MatchProperties { dest, src } => { CmdResult::MatchProperties { dest, src } => {
@ -1605,14 +1605,14 @@ impl OpenCADStudio {
self.tabs[i].scene.bump_entities(&changes); self.tabs[i].scene.bump_entities(&changes);
self.refresh_properties(); self.refresh_properties();
self.command_line self.command_line
.push_info(&format!("Properties matched to {} object(s).", dest.len())); .push_info(crate::tf!("Properties matched to {} object(s).", dest.len()).as_ref());
// Clear the consumed target selection and keep prompting. // Clear the consumed target selection and keep prompting.
self.tabs[i].scene.deselect_all(); self.tabs[i].scene.deselect_all();
if let Some(cmd) = &self.tabs[i].active_cmd { if let Some(cmd) = &self.tabs[i].active_cmd {
self.command_line.push_info(&cmd.prompt()); self.command_line.push_info(&cmd.prompt());
} }
} else { } else {
self.command_line.push_error("Source object not found."); self.command_line.push_error(crate::t!("Source object not found.").as_ref());
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
self.tabs[i].scene.clear_preview_wire(); self.tabs[i].scene.clear_preview_wire();
@ -1623,7 +1623,7 @@ impl OpenCADStudio {
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
self.tabs[i].scene.clear_preview_wire(); self.tabs[i].scene.clear_preview_wire();
if self.clipboard.is_empty() { if self.clipboard.is_empty() {
self.command_line.push_error("Clipboard is empty."); self.command_line.push_error(crate::t!("Clipboard is empty.").as_ref());
} else { } else {
let delta = base_pt - self.clipboard_base; let delta = base_pt - self.clipboard_base;
let translate = crate::command::EntityTransform::Translate(delta); let translate = crate::command::EntityTransform::Translate(delta);
@ -1640,7 +1640,7 @@ impl OpenCADStudio {
self.refresh_layer_panel(); self.refresh_layer_panel();
self.refresh_properties(); self.refresh_properties();
self.command_line self.command_line
.push_info(&format!("{count} object(s) pasted.")); .push_info(crate::tf!("{count} object(s) pasted.").as_ref());
} }
} }
CmdResult::CreateGroup { handles, name } => { CmdResult::CreateGroup { handles, name } => {
@ -1652,7 +1652,7 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.commit_group_undo(i, undo); self.commit_group_undo(i, undo);
self.command_line self.command_line
.push_info(&format!("Group \"{}\" created.", name)); .push_info(crate::tf!("Group \"{}\" created.", name).as_ref());
} }
CmdResult::DeleteGroups { handles } => { CmdResult::DeleteGroups { handles } => {
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
@ -1664,10 +1664,10 @@ impl OpenCADStudio {
self.commit_group_undo(i, undo); self.commit_group_undo(i, undo);
if count > 0 { if count > 0 {
self.command_line self.command_line
.push_info(&format!("{} group(s) dissolved.", count)); .push_info(crate::tf!("{} group(s) dissolved.", count).as_ref());
} else { } else {
self.command_line self.command_line
.push_info("No groups found for selected objects."); .push_info(crate::t!("No groups found for selected objects.").as_ref());
} }
} }
CmdResult::VpLayerUpdate { CmdResult::VpLayerUpdate {
@ -1747,14 +1747,14 @@ impl OpenCADStudio {
self.push_undo_snapshot(i, "VPLAYER"); self.push_undo_snapshot(i, "VPLAYER");
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
if frozen_count > 0 { if frozen_count > 0 {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"VPLAYER: {frozen_count} layer(s) frozen in viewport." "VPLAYER: {frozen_count} layer(s) frozen in viewport."
)); ).as_ref());
} }
if thawed_count > 0 { if thawed_count > 0 {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"VPLAYER: {thawed_count} layer(s) thawed in viewport." "VPLAYER: {thawed_count} layer(s) thawed in viewport."
)); ).as_ref());
} }
// Sync layer panel so VP freeze columns update immediately. // Sync layer panel so VP freeze columns update immediately.
let doc_layers = self.tabs[i].scene.document.layers.clone(); let doc_layers = self.tabs[i].scene.document.layers.clone();
@ -1778,7 +1778,7 @@ impl OpenCADStudio {
self.tabs[i] self.tabs[i]
.scene .scene
.zoom_to_window(p1.as_vec3(), p2.as_vec3()); .zoom_to_window(p1.as_vec3(), p2.as_vec3());
self.command_line.push_output("Zoom Window"); self.command_line.push_output(crate::t!("Zoom Window").as_ref());
} }
CmdResult::Measurement(msg) => { CmdResult::Measurement(msg) => {
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
@ -1844,7 +1844,7 @@ impl OpenCADStudio {
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
self.restore_pre_cmd_tangent(); self.restore_pre_cmd_tangent();
self.command_line.push_output("ALIGN: applied."); self.command_line.push_output(crate::t!("ALIGN: applied.").as_ref());
self.refresh_properties(); self.refresh_properties();
if let Some(pd) = pending { if let Some(pd) = pending {
self.commit_undo_delta(i, pd); self.commit_undo_delta(i, pd);
@ -1869,12 +1869,12 @@ impl OpenCADStudio {
self.tabs[i].scene.erase_entities(&[handle]); self.tabs[i].scene.erase_entities(&[handle]);
self.tabs[i].scene.add_entity(new_entity); self.tabs[i].scene.add_entity(new_entity);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output("LENGTHEN: applied."); self.command_line.push_output(crate::t!("LENGTHEN: applied.").as_ref());
self.refresh_properties(); self.refresh_properties();
} }
None => { None => {
self.command_line self.command_line
.push_error("LENGTHEN: entity type not supported."); .push_error(crate::t!("LENGTHEN: entity type not supported.").as_ref());
} }
} }
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
@ -1898,10 +1898,10 @@ impl OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("DIVIDE: {count} point(s) placed.")); .push_output(crate::tf!("DIVIDE: {count} point(s) placed.").as_ref());
} else { } else {
self.command_line self.command_line
.push_error("DIVIDE: entity type not supported or N < 2."); .push_error(crate::t!("DIVIDE: entity type not supported or N < 2.").as_ref());
} }
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
@ -1927,10 +1927,10 @@ impl OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("MEASURE: {count} point(s) placed.")); .push_output(crate::tf!("MEASURE: {count} point(s) placed.").as_ref());
} else { } else {
self.command_line self.command_line
.push_error("MEASURE: entity type not supported or distance too large."); .push_error(crate::t!("MEASURE: entity type not supported or distance too large.").as_ref());
} }
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
@ -1956,7 +1956,7 @@ impl OpenCADStudio {
} }
None => self None => self
.command_line .command_line
.push_error("PEDIT: cannot convert this entity."), .push_error(crate::t!("PEDIT: cannot convert this entity.").as_ref()),
} }
} }
_ => { _ => {
@ -1976,12 +1976,12 @@ impl OpenCADStudio {
self.tabs[i] self.tabs[i]
.scene .scene
.bump_entities(&[(handle, crate::scene::ChangeKind::Modified)]); .bump_entities(&[(handle, crate::scene::ChangeKind::Modified)]);
self.command_line.push_output("PEDIT: applied."); self.command_line.push_output(crate::t!("PEDIT: applied.").as_ref());
self.refresh_properties(); self.refresh_properties();
} else { } else {
self.discard_last_undo_entry(i); self.discard_last_undo_entry(i);
self.command_line self.command_line
.push_error("PEDIT: operation not applicable to this entity."); .push_error(crate::t!("PEDIT: operation not applicable to this entity.").as_ref());
} }
} }
} }
@ -2011,9 +2011,9 @@ impl OpenCADStudio {
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
self.restore_pre_cmd_tangent(); self.restore_pre_cmd_tangent();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"JOIN: {count_in} object(s) joined into {count_out}." "JOIN: {count_in} object(s) joined into {count_out}."
)); ).as_ref());
self.refresh_properties(); self.refresh_properties();
} }
None => { None => {
@ -2049,7 +2049,7 @@ impl OpenCADStudio {
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
self.restore_pre_cmd_tangent(); self.restore_pre_cmd_tangent();
self.command_line self.command_line
.push_output(&format!("BREAK: {} fragment(s).", count)); .push_output(crate::tf!("BREAK: {} fragment(s).", count).as_ref());
self.refresh_properties(); self.refresh_properties();
} }
None => { None => {
@ -2058,7 +2058,7 @@ impl OpenCADStudio {
self.tabs[i].scene.clear_preview_wire(); self.tabs[i].scene.clear_preview_wire();
self.restore_pre_cmd_tangent(); self.restore_pre_cmd_tangent();
self.command_line self.command_line
.push_error("BREAK: entity type not supported."); .push_error(crate::t!("BREAK: entity type not supported.").as_ref());
} }
} }
} }
@ -2072,7 +2072,7 @@ impl OpenCADStudio {
let y1 = p1.y.max(p2.y); let y1 = p1.y.max(p2.y);
self.plot_window = Some((x0, y0, x1, y1)); self.plot_window = Some((x0, y0, x1, y1));
self.command_line self.command_line
.push_output(&format!("Plot window: {x0:.2},{y0:.2} to {x1:.2},{y1:.2}")); .push_output(crate::tf!("Plot window: {x0:.2},{y0:.2} to {x1:.2},{y1:.2}").as_ref());
// Pick window closed the plot dialog so the viewport could // Pick window closed the plot dialog so the viewport could
// receive the two clicks — bring the dialog back with the // receive the two clicks — bring the dialog back with the
// window now active. // window now active.
@ -2089,9 +2089,9 @@ impl OpenCADStudio {
let x2 = p1.x.max(p2.x); let x2 = p1.x.max(p2.x);
let y2 = p1.y.max(p2.y); let y2 = p1.y.max(p2.y);
self.plot_window = Some((x1, y1, x2, y2)); self.plot_window = Some((x1, y1, x2, y2));
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Plot window: {x1:.2},{y1:.2} to {x2:.2},{y2:.2}" "Plot window: {x1:.2},{y1:.2} to {x2:.2},{y2:.2}"
)); ).as_ref());
self.plot_dialog.area = "Window".to_string(); self.plot_dialog.area = "Window".to_string();
self.active_modal = Some(super::ModalKind::Plot); self.active_modal = Some(super::ModalKind::Plot);
} }
@ -2127,7 +2127,7 @@ impl OpenCADStudio {
} }
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_output("STRETCH: nothing crosses the window."); .push_output(crate::t!("STRETCH: nothing crosses the window.").as_ref());
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
self.tabs[i].scene.clear_preview_wire(); self.tabs[i].scene.clear_preview_wire();
@ -2395,7 +2395,7 @@ impl OpenCADStudio {
self.tabs[i].scene.clear_preview_wire(); self.tabs[i].scene.clear_preview_wire();
self.restore_pre_cmd_tangent(); self.restore_pre_cmd_tangent();
self.command_line self.command_line
.push_output(&format!("STRETCH: {count} entity(ies) stretched.")); .push_output(crate::tf!("STRETCH: {count} entity(ies) stretched.").as_ref());
self.refresh_properties(); self.refresh_properties();
if let Some(pending) = pending { if let Some(pending) = pending {
self.commit_undo_delta(i, pending); self.commit_undo_delta(i, pending);
@ -2418,7 +2418,7 @@ impl OpenCADStudio {
.insert(handle, crate::scene::MeshLodSet::from_single(mesh)); .insert(handle, crate::scene::MeshLodSet::from_single(mesh));
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output("Solid created."); self.command_line.push_output(crate::t!("Solid created.").as_ref());
} }
if let Some(pd) = pending { if let Some(pd) = pending {
self.commit_undo_delta(i, pd); self.commit_undo_delta(i, pd);
@ -2492,15 +2492,15 @@ impl OpenCADStudio {
// ACIS geometry (else the solid is dropped by other CAD apps). // ACIS geometry (else the solid is dropped by other CAD apps).
self.tabs[i].scene.solid_models.insert(new_handle, solid); self.tabs[i].scene.solid_models.insert(new_handle, solid);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output("EXTRUDE: solid created."); self.command_line.push_output(crate::t!("EXTRUDE: solid created.").as_ref());
if let Some(pd) = pending { if let Some(pd) = pending {
self.commit_undo_delta(i, pd); self.commit_undo_delta(i, pd);
} }
} else { } else {
self.command_line.push_error("EXTRUDE: could not build profile. Select a closed 2D entity (Circle, LwPolyline, etc.)."); self.command_line.push_error(crate::t!("EXTRUDE: could not build profile. Select a closed 2D entity (Circle, LwPolyline, etc.).").as_ref());
} }
} else { } else {
self.command_line.push_error("EXTRUDE: entity not found."); self.command_line.push_error(crate::t!("EXTRUDE: entity not found.").as_ref());
} }
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
@ -2577,16 +2577,16 @@ impl OpenCADStudio {
.insert(new_handle, crate::scene::MeshLodSet::from_single(mesh)); .insert(new_handle, crate::scene::MeshLodSet::from_single(mesh));
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("REVOLVE: solid created ({:.0}°).", angle_deg)); .push_output(crate::tf!("REVOLVE: solid created ({:.0}°).", angle_deg).as_ref());
if let Some(pd) = pending { if let Some(pd) = pending {
self.commit_undo_delta(i, pd); self.commit_undo_delta(i, pd);
} }
} else { } else {
self.command_line self.command_line
.push_error("REVOLVE: could not revolve profile."); .push_error(crate::t!("REVOLVE: could not revolve profile.").as_ref());
} }
} else { } else {
self.command_line.push_error("REVOLVE: entity not found."); self.command_line.push_error(crate::t!("REVOLVE: entity not found.").as_ref());
} }
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
@ -2755,12 +2755,12 @@ impl OpenCADStudio {
.meshes .meshes
.insert(new_handle, crate::scene::MeshLodSet::from_single(mesh)); .insert(new_handle, crate::scene::MeshLodSet::from_single(mesh));
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output("SWEEP: solid created."); self.command_line.push_output(crate::t!("SWEEP: solid created.").as_ref());
if let Some(pd) = pending { if let Some(pd) = pending {
self.commit_undo_delta(i, pd); self.commit_undo_delta(i, pd);
} }
} else { } else {
self.command_line.push_error("SWEEP: could not sweep profile along path. Use a closed 2D profile and a Line or Polyline path."); self.command_line.push_error(crate::t!("SWEEP: could not sweep profile along path. Use a closed 2D profile and a Line or Polyline path.").as_ref());
} }
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
@ -2849,15 +2849,15 @@ impl OpenCADStudio {
.meshes .meshes
.insert(new_handle, crate::scene::MeshLodSet::from_single(mesh)); .insert(new_handle, crate::scene::MeshLodSet::from_single(mesh));
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"LOFT: solid created from {} profiles.", "LOFT: solid created from {} profiles.",
handles.len() handles.len()
)); ).as_ref());
if let Some(pd) = pending { if let Some(pd) = pending {
self.commit_undo_delta(i, pd); self.commit_undo_delta(i, pd);
} }
} else { } else {
self.command_line.push_error("LOFT: could not loft profiles. Ensure sections have the same edge count and are compatible."); self.command_line.push_error(crate::t!("LOFT: could not loft profiles. Ensure sections have the same edge count and are compatible.").as_ref());
} }
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
@ -2893,10 +2893,10 @@ impl OpenCADStudio {
// Re-add with updated model // Re-add with updated model
self.tabs[i].scene.add_hatch(model); self.tabs[i].scene.add_hatch(model);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output("HATCHEDIT: hatch updated."); self.command_line.push_output(crate::t!("HATCHEDIT: hatch updated.").as_ref());
} else { } else {
self.command_line self.command_line
.push_error("HATCHEDIT: hatch entity not found."); .push_error(crate::t!("HATCHEDIT: hatch entity not found.").as_ref());
} }
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
@ -2942,7 +2942,7 @@ impl OpenCADStudio {
crate::app::text_inline::can_edit_text(handle, &self.tabs[i].scene.document); crate::app::text_inline::can_edit_text(handle, &self.tabs[i].scene.document);
if !is_editable { if !is_editable {
self.command_line self.command_line
.push_error("TEXTEDIT: selected entity is not text."); .push_error(crate::t!("TEXTEDIT: selected entity is not text.").as_ref());
let prompt = self.tabs[i].active_cmd.as_ref().map(|c| c.prompt()); let prompt = self.tabs[i].active_cmd.as_ref().map(|c| c.prompt());
if let Some(p) = prompt { if let Some(p) = prompt {
self.command_line.push_info(&p); self.command_line.push_info(&p);
@ -2970,7 +2970,7 @@ impl OpenCADStudio {
self.texteditmode = val; self.texteditmode = val;
let display_val = if val { 1 } else { 0 }; let display_val = if val { 1 } else { 0 };
self.command_line self.command_line
.push_output(&format!("TEXTEDITMODE set to {display_val}")); .push_output(crate::tf!("TEXTEDITMODE set to {display_val}").as_ref());
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;
self.tabs[i].scene.clear_preview_wire(); self.tabs[i].scene.clear_preview_wire();
@ -3014,11 +3014,11 @@ impl OpenCADStudio {
} }
if updated { if updated {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output("DDEDIT: text updated."); self.command_line.push_output(crate::t!("DDEDIT: text updated.").as_ref());
} else { } else {
self.discard_last_undo_entry(i); self.discard_last_undo_entry(i);
self.command_line self.command_line
.push_error("DDEDIT: entity type not supported."); .push_error(crate::t!("DDEDIT: entity type not supported.").as_ref());
} }
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None; self.tabs[i].snap_result = None;

View file

@ -43,12 +43,12 @@ impl OpenCADStudio {
} else { } else {
"model space" "model space"
}; };
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Base point ({}, {}, {}) set for {space}.", "Base point ({}, {}, {}) set for {space}.",
nums[0], nums[1], z nums[0], nums[1], z
)); ).as_ref());
} else { } else {
self.command_line.push_error("Usage: BASE <x> <y> [z]"); self.command_line.push_error(crate::t!("Usage: BASE <x> <y> [z]").as_ref());
} }
} }
} }
@ -60,7 +60,7 @@ impl OpenCADStudio {
return match self.mtext_selected_text() { return match self.mtext_selected_text() {
Some(text) => { Some(text) => {
self.command_line self.command_line
.push_info("Copied selected text to clipboard."); .push_info(crate::t!("Copied selected text to clipboard.").as_ref());
Some(iced::clipboard::write(text).discard()) Some(iced::clipboard::write(text).discard())
} }
None => Some(Task::none()), None => Some(Task::none()),
@ -91,10 +91,10 @@ impl OpenCADStudio {
&self.tabs[i].scene.document, &self.tabs[i].scene.document,
&self.clipboard, &self.clipboard,
); );
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"{} object(s) copied to clipboard.", "{} object(s) copied to clipboard.",
self.clipboard.len() self.clipboard.len()
)); ).as_ref());
} }
} }
@ -145,12 +145,12 @@ impl OpenCADStudio {
&self.tabs[i].scene.document, &self.tabs[i].scene.document,
&self.clipboard, &self.clipboard,
); );
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"{} object(s) copied to clipboard (base {:.3},{:.3}).", "{} object(s) copied to clipboard (base {:.3},{:.3}).",
self.clipboard.len(), self.clipboard.len(),
base.x, base.x,
base.y base.y
)); ).as_ref());
} }
} }
@ -187,13 +187,13 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
self.command_line self.command_line
.push_info(&format!("{} object(s) cut to clipboard.", count)); .push_info(crate::tf!("{} object(s) cut to clipboard.", count).as_ref());
} }
} }
"PASTECLIP" => { "PASTECLIP" => {
if self.clipboard.is_empty() { if self.clipboard.is_empty() {
self.command_line.push_error("Clipboard is empty."); self.command_line.push_error(crate::t!("Clipboard is empty.").as_ref());
} else { } else {
let wires = self.tabs[i].scene.wires_for_entities(&self.clipboard); let wires = self.tabs[i].scene.wires_for_entities(&self.clipboard);
let base = self.clipboard_base; let base = self.clipboard_base;
@ -210,7 +210,7 @@ impl OpenCADStudio {
"PASTEORIG" => { "PASTEORIG" => {
if self.clipboard.is_empty() { if self.clipboard.is_empty() {
self.command_line self.command_line
.push_error("PASTEORIG: clipboard is empty."); .push_error(crate::t!("PASTEORIG: clipboard is empty.").as_ref());
} else { } else {
let count = self.clipboard.len(); let count = self.clipboard.len();
self.push_undo_snapshot(i, "PASTEORIG"); self.push_undo_snapshot(i, "PASTEORIG");
@ -219,10 +219,10 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_layer_panel(); self.refresh_layer_panel();
self.refresh_properties(); self.refresh_properties();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"PASTEORIG: {} object(s) pasted at original coordinates.", "PASTEORIG: {} object(s) pasted at original coordinates.",
count count
)); ).as_ref());
} }
} }
@ -231,7 +231,7 @@ impl OpenCADStudio {
"PASTEBLOCK" => { "PASTEBLOCK" => {
if self.clipboard.is_empty() { if self.clipboard.is_empty() {
self.command_line self.command_line
.push_error("PASTEBLOCK: clipboard is empty."); .push_error(crate::t!("PASTEBLOCK: clipboard is empty.").as_ref());
} else { } else {
self.push_undo_snapshot(i, "PASTEBLOCK"); self.push_undo_snapshot(i, "PASTEBLOCK");
self.merge_clipboard_deps(i); self.merge_clipboard_deps(i);
@ -294,7 +294,7 @@ impl OpenCADStudio {
self.command_line.push_info(&cmd.prompt()); self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd)); self.tabs[i].active_cmd = Some(Box::new(cmd));
} }
Err(e) => self.command_line.push_error(&format!("PASTEBLOCK: {e}")), Err(e) => self.command_line.push_error(crate::tf!("PASTEBLOCK: {e}").as_ref()),
} }
} }
} }
@ -323,7 +323,7 @@ impl OpenCADStudio {
let blocks = self.tabs[i].scene.custom_block_names(); let blocks = self.tabs[i].scene.custom_block_names();
if blocks.is_empty() { if blocks.is_empty() {
self.command_line self.command_line
.push_error("No user-defined blocks found in this drawing."); .push_error(crate::t!("No user-defined blocks found in this drawing.").as_ref());
} else { } else {
use crate::modules::insert::insert_block::InsertBlockCommand; use crate::modules::insert::insert_block::InsertBlockCommand;
let cmd = InsertBlockCommand::new(blocks); let cmd = InsertBlockCommand::new(blocks);
@ -336,7 +336,7 @@ impl OpenCADStudio {
let blocks = self.tabs[i].scene.custom_block_names(); let blocks = self.tabs[i].scene.custom_block_names();
if blocks.is_empty() { if blocks.is_empty() {
self.command_line self.command_line
.push_error("No user-defined blocks found in this drawing."); .push_error(crate::t!("No user-defined blocks found in this drawing.").as_ref());
} else { } else {
use crate::modules::insert::minsert::MinsertCommand; use crate::modules::insert::minsert::MinsertCommand;
let cmd = MinsertCommand::new(blocks); let cmd = MinsertCommand::new(blocks);
@ -359,20 +359,20 @@ impl OpenCADStudio {
let arg = cmd.trim_start_matches("ATTSYNC").trim(); let arg = cmd.trim_start_matches("ATTSYNC").trim();
let blocks = self.tabs[i].scene.custom_block_names(); let blocks = self.tabs[i].scene.custom_block_names();
if arg.is_empty() { if arg.is_empty() {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"Usage: ATTSYNC <block name>. Blocks: {}", "Usage: ATTSYNC <block name>. Blocks: {}",
if blocks.is_empty() { if blocks.is_empty() {
"(none)".to_string() "(none)".to_string()
} else { } else {
blocks.join(", ") blocks.join(", ")
} }
)); ).as_ref());
return Some(Task::none()); return Some(Task::none());
} }
let Some(block) = blocks.iter().find(|b| b.eq_ignore_ascii_case(arg)).cloned() let Some(block) = blocks.iter().find(|b| b.eq_ignore_ascii_case(arg)).cloned()
else { else {
self.command_line self.command_line
.push_error(&format!("ATTSYNC: no block named \"{arg}\".")); .push_error(crate::tf!("ATTSYNC: no block named \"{arg}\".").as_ref());
return Some(Task::none()); return Some(Task::none());
}; };
// Gather the block's attribute definitions (tag, default value). // Gather the block's attribute definitions (tag, default value).
@ -422,10 +422,10 @@ impl OpenCADStudio {
} }
self.tabs[i].scene.bump_entities(&changes); self.tabs[i].scene.bump_entities(&changes);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"ATTSYNC: synchronised {synced} insert(s) of \"{block}\" against {} attribute definition(s).", "ATTSYNC: synchronised {synced} insert(s) of \"{block}\" against {} attribute definition(s).",
attdefs.len() attdefs.len()
)); ).as_ref());
} }
// ADCENTER / CONTENTBROWSER — report the drawing's named content // ADCENTER / CONTENTBROWSER — report the drawing's named content
@ -440,7 +440,7 @@ impl OpenCADStudio {
.names() .names()
.map(|s| s.to_string()) .map(|s| s.to_string())
.collect(); .collect();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Blocks ({}): {}", "Blocks ({}): {}",
blocks.len(), blocks.len(),
if blocks.is_empty() { if blocks.is_empty() {
@ -448,12 +448,12 @@ impl OpenCADStudio {
} else { } else {
blocks.join(", ") blocks.join(", ")
} }
)); ).as_ref());
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Layers ({}): {}", "Layers ({}): {}",
layers.len(), layers.len(),
layers.join(", ") layers.join(", ")
)); ).as_ref());
} }
// BLOCKPALETTE — list the blocks available to insert. // BLOCKPALETTE — list the blocks available to insert.
@ -464,11 +464,11 @@ impl OpenCADStudio {
"No user-defined blocks. Define one with BLOCK, then INSERT it.", "No user-defined blocks. Define one with BLOCK, then INSERT it.",
); );
} else { } else {
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Blocks ({}) — insert with INSERT <name>: {}", "Blocks ({}) — insert with INSERT <name>: {}",
blocks.len(), blocks.len(),
blocks.join(", ") blocks.join(", ")
)); ).as_ref());
} }
} }
@ -527,9 +527,9 @@ impl OpenCADStudio {
.collect(); .collect();
if xrefs.is_empty() { if xrefs.is_empty() {
self.command_line self.command_line
.push_output("XREF No external references in this drawing."); .push_output(crate::t!("XREF No external references in this drawing.").as_ref());
} else { } else {
self.command_line.push_output("XREF External references:"); self.command_line.push_output(crate::t!("XREF External references:").as_ref());
for line in xrefs { for line in xrefs {
self.command_line.push_output(&line); self.command_line.push_output(&line);
} }
@ -548,19 +548,19 @@ impl OpenCADStudio {
match info.status { match info.status {
crate::io::xref::XrefStatus::Loaded => { crate::io::xref::XrefStatus::Loaded => {
self.command_line self.command_line
.push_output(&format!("XREF Reloaded \"{}\"", info.name)); .push_output(crate::tf!("XREF Reloaded \"{}\"", info.name).as_ref());
} }
crate::io::xref::XrefStatus::NotFound => { crate::io::xref::XrefStatus::NotFound => {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"XREF Not found: \"{}\" ({})", "XREF Not found: \"{}\" ({})",
info.name, info.path info.name, info.path
)); ).as_ref());
} }
crate::io::xref::XrefStatus::Unloaded => { crate::io::xref::XrefStatus::Unloaded => {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"XREF Unloaded (skipped): \"{}\"", "XREF Unloaded (skipped): \"{}\"",
info.name info.name
)); ).as_ref());
} }
} }
} }
@ -574,7 +574,7 @@ impl OpenCADStudio {
} }
} else { } else {
self.command_line self.command_line
.push_error("XREF Save the drawing first to resolve relative XREF paths."); .push_error(crate::t!("XREF Save the drawing first to resolve relative XREF paths.").as_ref());
} }
} }
@ -604,7 +604,7 @@ impl OpenCADStudio {
} }
None => self None => self
.command_line .command_line
.push_error("XOPEN: select an external reference (xref) to open."), .push_error(crate::t!("XOPEN: select an external reference (xref) to open.").as_ref()),
} }
} }
@ -622,7 +622,7 @@ impl OpenCADStudio {
.collect(); .collect();
if inserts.is_empty() { if inserts.is_empty() {
self.command_line self.command_line
.push_error("NCOPY: select a block reference first."); .push_error(crate::t!("NCOPY: select a block reference first.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
self.push_undo_snapshot(i, "NCOPY"); self.push_undo_snapshot(i, "NCOPY");
@ -638,9 +638,9 @@ impl OpenCADStudio {
} }
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"NCOPY: copied {n} nested object(s) into model space (blocks kept)." "NCOPY: copied {n} nested object(s) into model space (blocks kept)."
)); ).as_ref());
} }
_ => return None, _ => return None,

View file

@ -85,7 +85,7 @@ impl OpenCADStudio {
.collect(); .collect();
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_error("JUSTIFYTEXT: select text objects first."); .push_error(crate::t!("JUSTIFYTEXT: select text objects first.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
if opt.is_empty() { if opt.is_empty() {
@ -130,7 +130,7 @@ impl OpenCADStudio {
}; };
if text_align.is_none() && mtext_ap.is_none() { if text_align.is_none() && mtext_ap.is_none() {
self.command_line self.command_line
.push_error("JUSTIFYTEXT: unknown justification option."); .push_error(crate::t!("JUSTIFYTEXT: unknown justification option.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
self.push_undo_snapshot(i, "JUSTIFYTEXT"); self.push_undo_snapshot(i, "JUSTIFYTEXT");
@ -175,7 +175,7 @@ impl OpenCADStudio {
.collect(); .collect();
self.tabs[i].scene.bump_entities(&changes); self.tabs[i].scene.bump_entities(&changes);
self.command_line self.command_line
.push_output(&format!("JUSTIFYTEXT: updated {n} text object(s).")); .push_output(crate::tf!("JUSTIFYTEXT: updated {n} text object(s).").as_ref());
} }
// TCASE <Upper|Lower|Sentence|Title> — change the case of the text in // TCASE <Upper|Lower|Sentence|Title> — change the case of the text in
@ -207,7 +207,7 @@ impl OpenCADStudio {
.collect(); .collect();
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_error("TCASE: select text objects first."); .push_error(crate::t!("TCASE: select text objects first.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
if !matches!( if !matches!(
@ -223,7 +223,7 @@ impl OpenCADStudio {
| "TITLE" | "TITLE"
) { ) {
self.command_line self.command_line
.push_info("Usage: TCASE <Upper|Lower|Sentence|Title>"); .push_info(crate::t!("Usage: TCASE <Upper|Lower|Sentence|Title>").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
let conv = move |s: &str| -> String { let conv = move |s: &str| -> String {
@ -263,7 +263,7 @@ impl OpenCADStudio {
.collect(); .collect();
self.tabs[i].scene.bump_entities(&changes); self.tabs[i].scene.bump_entities(&changes);
self.command_line self.command_line
.push_output(&format!("TCASE: updated {n} text object(s).")); .push_output(crate::tf!("TCASE: updated {n} text object(s).").as_ref());
} }
// TEXTMASK — place a wipeout mask sized to each selected text object's // TEXTMASK — place a wipeout mask sized to each selected text object's
@ -296,7 +296,7 @@ impl OpenCADStudio {
.collect(); .collect();
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_error("TEXTMASK: select text objects first."); .push_error(crate::t!("TEXTMASK: select text objects first.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
self.push_undo_snapshot(i, "TEXTMASK"); self.push_undo_snapshot(i, "TEXTMASK");
@ -339,7 +339,7 @@ impl OpenCADStudio {
.replace_selection(handles.iter().cloned().collect()); .replace_selection(handles.iter().cloned().collect());
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("TEXTMASK: masked {n} text object(s).")); .push_output(crate::tf!("TEXTMASK: masked {n} text object(s).").as_ref());
return self.dispatch_view("DRAWORDER FRONT", i); return self.dispatch_view("DRAWORDER FRONT", i);
} }
@ -373,7 +373,7 @@ impl OpenCADStudio {
.collect(); .collect();
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_error("TEXTFIT: select single-line text first."); .push_error(crate::t!("TEXTFIT: select single-line text first.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
self.push_undo_snapshot(i, "TEXTFIT"); self.push_undo_snapshot(i, "TEXTFIT");
@ -412,9 +412,9 @@ impl OpenCADStudio {
.map(|handle| (handle, crate::scene::ChangeKind::Modified)) .map(|handle| (handle, crate::scene::ChangeKind::Modified))
.collect(); .collect();
self.tabs[i].scene.bump_entities(&changes); self.tabs[i].scene.bump_entities(&changes);
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"TEXTFIT: fitted {n} text object(s) to width {target}." "TEXTFIT: fitted {n} text object(s) to width {target}."
)); ).as_ref());
} }
// (text sequential numbering) // (text sequential numbering)
@ -450,7 +450,7 @@ impl OpenCADStudio {
.collect(); .collect();
if texts.is_empty() { if texts.is_empty() {
self.command_line self.command_line
.push_error("TCOUNT: select single-line text first."); .push_error(crate::t!("TCOUNT: select single-line text first.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
// Reading order: higher Y first, then smaller X. // Reading order: higher Y first, then smaller X.
@ -478,10 +478,10 @@ impl OpenCADStudio {
.map(|(handle, _, _)| (*handle, crate::scene::ChangeKind::Modified)) .map(|(handle, _, _)| (*handle, crate::scene::ChangeKind::Modified))
.collect(); .collect();
self.tabs[i].scene.bump_entities(&changes); self.tabs[i].scene.bump_entities(&changes);
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"TCOUNT: numbered {} text object(s) from {start}.", "TCOUNT: numbered {} text object(s) from {start}.",
texts.len() texts.len()
)); ).as_ref());
} }
"MLEADER" => { "MLEADER" => {
@ -575,7 +575,7 @@ impl OpenCADStudio {
} }
if pts.len() < 2 { if pts.len() < 2 {
self.command_line self.command_line
.push_error("QDIM: no dimensionable endpoints in the selection."); .push_error(crate::t!("QDIM: no dimensionable endpoints in the selection.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
// Choose the dimension axis from the points' spread: a wider X // Choose the dimension axis from the points' spread: a wider X
@ -596,7 +596,7 @@ impl OpenCADStudio {
} }
if pts.len() < 2 { if pts.len() < 2 {
self.command_line self.command_line
.push_error("QDIM: endpoints collapse to a single position."); .push_error(crate::t!("QDIM: endpoints collapse to a single position.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
self.push_undo_snapshot(i, "QDIM"); self.push_undo_snapshot(i, "QDIM");
@ -628,7 +628,7 @@ impl OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("QDIM {made} dimensions created.")); .push_output(crate::tf!("QDIM {made} dimensions created.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
@ -697,28 +697,28 @@ impl OpenCADStudio {
"ZOOM EXTENTS ALL" | "ZOOM EXTENTS ALL VIEWPORTS" | "ZEA" => { "ZOOM EXTENTS ALL" | "ZOOM EXTENTS ALL VIEWPORTS" | "ZEA" => {
self.tabs[i].scene.fit_all_model_viewports(); self.tabs[i].scene.fit_all_model_viewports();
self.command_line.push_output("Zoom Extents — All Viewports"); self.command_line.push_output(crate::t!("Zoom Extents — All Viewports").as_ref());
} }
"ZOOM EXTENTS" | "ZOOMEXTENTS" | "ZE" => { "ZOOM EXTENTS" | "ZOOMEXTENTS" | "ZE" => {
self.tabs[i].scene.fit_all(); self.tabs[i].scene.fit_all();
self.command_line.push_output("Zoom Extents"); self.command_line.push_output(crate::t!("Zoom Extents").as_ref());
} }
"ZOOM IN" | "ZI" => { "ZOOM IN" | "ZI" => {
self.tabs[i].scene.zoom_camera(1.0 / 1.5); self.tabs[i].scene.zoom_camera(1.0 / 1.5);
self.command_line.push_output("Zoom In"); self.command_line.push_output(crate::t!("Zoom In").as_ref());
} }
"ZOOM OUT" | "ZO" => { "ZOOM OUT" | "ZO" => {
self.tabs[i].scene.zoom_camera(1.5); self.tabs[i].scene.zoom_camera(1.5);
self.command_line.push_output("Zoom Out"); self.command_line.push_output(crate::t!("Zoom Out").as_ref());
} }
// ZOOM ALL — fit the configured drawing limits. // ZOOM ALL — fit the configured drawing limits.
"ZOOM ALL" | "ZOOM A" | "ZA" => { "ZOOM ALL" | "ZOOM A" | "ZA" => {
self.tabs[i].scene.fit_all_with_limits(); self.tabs[i].scene.fit_all_with_limits();
self.command_line.push_output("Zoom All"); self.command_line.push_output(crate::t!("Zoom All").as_ref());
} }
// ZOOM SCALE — set zoom factor (e.g. "ZOOM SCALE 2" or "ZS 0.5") // ZOOM SCALE — set zoom factor (e.g. "ZOOM SCALE 2" or "ZS 0.5")
@ -731,7 +731,7 @@ impl OpenCADStudio {
if factor > 0.0 { if factor > 0.0 {
self.tabs[i].scene.zoom_camera(1.0 / factor); self.tabs[i].scene.zoom_camera(1.0 / factor);
self.command_line self.command_line
.push_output(&format!("Zoom Scale ×{factor:.3}")); .push_output(crate::tf!("Zoom Scale ×{factor:.3}").as_ref());
} }
} }
} }
@ -992,10 +992,10 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
self.command_line self.command_line
.push_output(&format!("{exploded} object(s) exploded.")); .push_output(crate::tf!("{exploded} object(s) exploded.").as_ref());
} else { } else {
self.command_line self.command_line
.push_info("EXPLODE: no explodable objects selected."); .push_info(crate::t!("EXPLODE: no explodable objects selected.").as_ref());
} }
} }
} }
@ -1108,11 +1108,11 @@ impl OpenCADStudio {
}); });
let Some((center, radius)) = found else { let Some((center, radius)) = found else {
self.command_line self.command_line
.push_error("DIMJOGGED: select an arc or circle first."); .push_error(crate::t!("DIMJOGGED: select an arc or circle first.").as_ref());
return None; return None;
}; };
if radius <= 0.0 { if radius <= 0.0 {
self.command_line.push_error("DIMJOGGED: invalid radius."); self.command_line.push_error(crate::t!("DIMJOGGED: invalid radius.").as_ref());
return None; return None;
} }
let k = std::f64::consts::FRAC_1_SQRT_2; let k = std::f64::consts::FRAC_1_SQRT_2;
@ -1129,7 +1129,7 @@ impl OpenCADStudio {
.add_entity(acadrust::EntityType::Dimension(Dimension::Radius(dim))); .add_entity(acadrust::EntityType::Dimension(Dimension::Radius(dim)));
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output("DIMJOGGED: created a jogged radius dimension."); .push_output(crate::t!("DIMJOGGED: created a jogged radius dimension.").as_ref());
} }
// ARCTEXT <text> — lay the text out as one Text entity per character // ARCTEXT <text> — lay the text out as one Text entity per character
@ -1166,12 +1166,12 @@ impl OpenCADStudio {
}); });
let Some(arc) = arc else { let Some(arc) = arc else {
self.command_line self.command_line
.push_error("ARCTEXT: select an arc first."); .push_error(crate::t!("ARCTEXT: select an arc first.").as_ref());
return None; return None;
}; };
let chars: Vec<char> = text.chars().filter(|c| !c.is_control()).collect(); let chars: Vec<char> = text.chars().filter(|c| !c.is_control()).collect();
if chars.is_empty() { if chars.is_empty() {
self.command_line.push_error("ARCTEXT: no printable text."); self.command_line.push_error(crate::t!("ARCTEXT: no printable text.").as_ref());
return None; return None;
} }
let n = chars.len(); let n = chars.len();
@ -1195,7 +1195,7 @@ impl OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("ARCTEXT: placed {n} character(s) along the arc.")); .push_output(crate::tf!("ARCTEXT: placed {n} character(s) along the arc.").as_ref());
} }
_ => return None, _ => return None,

View file

@ -8,7 +8,7 @@ impl OpenCADStudio {
"PAN" => { "PAN" => {
self.tabs[i].pan_mode = true; self.tabs[i].pan_mode = true;
self.command_line self.command_line
.push_output("PAN: drag with the left mouse button. Press Esc to exit."); .push_output(crate::t!("PAN: drag with the left mouse button. Press Esc to exit.").as_ref());
} }
// ── TABLE cell editing ───────────────────────────────────────────── // ── TABLE cell editing ─────────────────────────────────────────────
@ -46,18 +46,18 @@ impl OpenCADStudio {
if found { if found {
self.push_undo_snapshot(i, "TABLE CELL"); self.push_undo_snapshot(i, "TABLE CELL");
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"TABLE CELL: set [{row},{col}] = \"{text}\"." "TABLE CELL: set [{row},{col}] = \"{text}\"."
)); ).as_ref());
} else { } else {
self.command_line.push_error( self.command_line.push_error(
"TABLE CELL: select a Table entity first, or row/col out of range." crate::t!("TABLE CELL: select a Table entity first, or row/col out of range.").as_ref()
); );
} }
} }
_ => { _ => {
self.command_line self.command_line
.push_info("Usage: TABLE CELL <row> <col> <text>"); .push_info(crate::t!("Usage: TABLE CELL <row> <col> <text>").as_ref());
} }
} }
} else { } else {
@ -111,9 +111,9 @@ impl OpenCADStudio {
} }
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"UCSICON {sub}: updated {count} viewport(s) + model space." "UCSICON {sub}: updated {count} viewport(s) + model space."
)); ).as_ref());
} }
"" => { "" => {
// Bare UCSICON toggles visibility. // Bare UCSICON toggles visibility.
@ -128,11 +128,11 @@ impl OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
let state = if visible { "ON" } else { "OFF" }; let state = if visible { "ON" } else { "OFF" };
self.command_line.push_output(&format!("UCSICON {state}")); self.command_line.push_output(crate::tf!("UCSICON {state}").as_ref());
} }
_ => { _ => {
self.command_line self.command_line
.push_info("Usage: UCSICON ON | OFF | NOORIGIN | ORIGIN"); .push_info(crate::t!("Usage: UCSICON ON | OFF | NOORIGIN | ORIGIN").as_ref());
} }
} }
} }
@ -172,12 +172,12 @@ impl OpenCADStudio {
tokens.iter().map(|value| value.parse()).collect(); tokens.iter().map(|value| value.parse()).collect();
let Ok(values) = values else { let Ok(values) = values else {
self.command_line self.command_line
.push_error("LIMITS: four numeric coordinates required."); .push_error(crate::t!("LIMITS: four numeric coordinates required.").as_ref());
return Some(Task::none()); return Some(Task::none());
}; };
if tokens.len() != 4 || !values.iter().all(|value| value.is_finite()) { if tokens.len() != 4 || !values.iter().all(|value| value.is_finite()) {
self.command_line self.command_line
.push_error("LIMITS: four finite numeric coordinates required."); .push_error(crate::t!("LIMITS: four finite numeric coordinates required.").as_ref());
} else { } else {
let first = glam::DVec2::new(values[0], values[1]); let first = glam::DVec2::new(values[0], values[1]);
let opposite = glam::DVec2::new(values[2], values[3]); let opposite = glam::DVec2::new(values[2], values[3]);
@ -185,15 +185,15 @@ impl OpenCADStudio {
let max = first.max(opposite); let max = first.max(opposite);
if min.x == max.x || min.y == max.y { if min.x == max.x || min.y == max.y {
self.command_line self.command_line
.push_error("LIMITS: corners must define a non-zero area."); .push_error(crate::t!("LIMITS: corners must define a non-zero area.").as_ref());
} else { } else {
self.push_undo_snapshot(i, "LIMITS"); self.push_undo_snapshot(i, "LIMITS");
self.tabs[i].scene.set_current_drawing_limits(min, max); self.tabs[i].scene.set_current_drawing_limits(min, max);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Drawing limits: {:.4},{:.4} to {:.4},{:.4}.", "Drawing limits: {:.4},{:.4} to {:.4},{:.4}.",
min.x, min.y, max.x, max.y min.x, min.y, max.x, max.y
)); ).as_ref());
} }
} }
} }
@ -249,13 +249,13 @@ impl OpenCADStudio {
// ── TOOLPALETTES — not yet implemented ─────────────────────────────── // ── TOOLPALETTES — not yet implemented ───────────────────────────────
"TOOLPALETTES" => { "TOOLPALETTES" => {
self.command_line self.command_line
.push_info("TOOLPALETTES: Tool Palettes not yet implemented."); .push_info(crate::t!("TOOLPALETTES: Tool Palettes not yet implemented.").as_ref());
} }
// ── SHEETSET — not yet implemented ─────────────────────────────────── // ── SHEETSET — not yet implemented ───────────────────────────────────
"SHEETSET" => { "SHEETSET" => {
self.command_line self.command_line
.push_info("SHEETSET: Sheet Set Manager not yet implemented."); .push_info(crate::t!("SHEETSET: Sheet Set Manager not yet implemented.").as_ref());
} }
// ── XDATA — read/write extended entity data ────────────────────────── // ── XDATA — read/write extended entity data ──────────────────────────
@ -288,7 +288,7 @@ impl OpenCADStudio {
.collect(); .collect();
if selected_handles.is_empty() { if selected_handles.is_empty() {
self.command_line self.command_line
.push_error("XDATA: select entities first."); .push_error(crate::t!("XDATA: select entities first.").as_ref());
} else { } else {
match sub.as_str() { match sub.as_str() {
"LIST" | "" => { "LIST" | "" => {
@ -297,18 +297,18 @@ impl OpenCADStudio {
let xd = &entity.common().extended_data; let xd = &entity.common().extended_data;
if xd.is_empty() { if xd.is_empty() {
self.command_line self.command_line
.push_output(&format!(" {:x}: no xdata.", sh.value())); .push_output(crate::tf!(" {:x}: no xdata.", sh.value()).as_ref());
} else { } else {
for rec in xd.records() { for rec in xd.records() {
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" {:x} [{}]: {} value(s)", " {:x} [{}]: {} value(s)",
sh.value(), sh.value(),
rec.application_name, rec.application_name,
rec.values.len() rec.values.len()
)); ).as_ref());
for v in &rec.values { for v in &rec.values {
self.command_line self.command_line
.push_output(&format!(" {:?}", v)); .push_output(crate::tf!(" {:?}", v).as_ref());
} }
} }
} }
@ -329,10 +329,10 @@ impl OpenCADStudio {
} }
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"XDATA: set [{app}] = \"{val}\" on {} entity/entities.", "XDATA: set [{app}] = \"{val}\" on {} entity/entities.",
selected_handles.len() selected_handles.len()
)); ).as_ref());
} }
"CLEAR" => { "CLEAR" => {
let app_filter = parts.get(1).copied(); let app_filter = parts.get(1).copied();
@ -360,11 +360,11 @@ impl OpenCADStudio {
} }
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output("XDATA: cleared."); self.command_line.push_output(crate::t!("XDATA: cleared.").as_ref());
} }
_ => { _ => {
self.command_line self.command_line
.push_info("Usage: XDATA LIST | SET <app> <value> | CLEAR [app]"); .push_info(crate::t!("Usage: XDATA LIST | SET <app> <value> | CLEAR [app]").as_ref());
} }
} }
} }
@ -486,7 +486,7 @@ impl OpenCADStudio {
} }
_ => { _ => {
self.command_line self.command_line
.push_error("Usage: PLOTSTYLE [LOAD | CLEAR | STATUS]"); .push_error(crate::t!("Usage: PLOTSTYLE [LOAD | CLEAR | STATUS]").as_ref());
} }
} }
} }
@ -529,7 +529,7 @@ impl OpenCADStudio {
.collect(); .collect();
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_error("UNDERLAY: select underlay entities first."); .push_error(crate::t!("UNDERLAY: select underlay entities first.").as_ref());
} else { } else {
let parts: Vec<&str> = sub.splitn(2, char::is_whitespace).collect(); let parts: Vec<&str> = sub.splitn(2, char::is_whitespace).collect();
let action = parts.first().copied().unwrap_or(""); let action = parts.first().copied().unwrap_or("");
@ -589,7 +589,7 @@ impl OpenCADStudio {
}, },
_ => { _ => {
// No sub-command: print status. // No sub-command: print status.
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Underlay {:x}: fade={}, contrast={}, on={}, clip={}, mono={}", "Underlay {:x}: fade={}, contrast={}, on={}, clip={}, mono={}",
h.value(), h.value(),
ul.fade, ul.fade,
@ -597,7 +597,7 @@ impl OpenCADStudio {
ul.is_on(), ul.is_on(),
ul.is_clipping(), ul.is_clipping(),
ul.is_monochrome(), ul.is_monochrome(),
)); ).as_ref());
} }
} }
} }
@ -605,10 +605,10 @@ impl OpenCADStudio {
if changed > 0 { if changed > 0 {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_info(&format!("Updated {changed} underlay(s).")); .push_info(crate::tf!("Updated {changed} underlay(s).").as_ref());
} else if !action.is_empty() { } else if !action.is_empty() {
self.command_line.push_error( self.command_line.push_error(
"Usage: UNDERLAY [FADE <n>|CONTRAST <n>|ON|OFF|CLIP ON|OFF|MONO ON|OFF]" crate::t!("Usage: UNDERLAY [FADE <n>|CONTRAST <n>|ON|OFF|CLIP ON|OFF|MONO ON|OFF]").as_ref()
); );
} }
} }
@ -635,13 +635,13 @@ impl OpenCADStudio {
.collect(); .collect();
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_error("OBJECTSCALE: select objects first."); .push_error(crate::t!("OBJECTSCALE: select objects first.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
self.push_undo_snapshot(i, "OBJECTSCALE"); self.push_undo_snapshot(i, "OBJECTSCALE");
let Some(scale) = self.tabs[i].scene.creation_annotation_scale_handle() else { let Some(scale) = self.tabs[i].scene.creation_annotation_scale_handle() else {
self.command_line self.command_line
.push_error("OBJECTSCALE: the active annotation scale is unavailable."); .push_error(crate::t!("OBJECTSCALE: the active annotation scale is unavailable.").as_ref());
return Some(Task::none()); return Some(Task::none());
}; };
let mut n = 0usize; let mut n = 0usize;
@ -665,9 +665,9 @@ impl OpenCADStudio {
.collect(); .collect();
self.tabs[i].scene.bump_entities(&changes); self.tabs[i].scene.bump_entities(&changes);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"OBJECTSCALE: added the active scale to {n} object(s)." "OBJECTSCALE: added the active scale to {n} object(s)."
)); ).as_ref());
return Some(Task::none()); return Some(Task::none());
} }
@ -688,13 +688,13 @@ impl OpenCADStudio {
use acadrust::xdata::{ExtendedDataRecord, XDataValue}; use acadrust::xdata::{ExtendedDataRecord, XDataValue};
let url = cmd.strip_prefix("HYPERLINK").unwrap_or("").trim().to_string(); let url = cmd.strip_prefix("HYPERLINK").unwrap_or("").trim().to_string();
if url.is_empty() { if url.is_empty() {
self.command_line.push_info("Usage: HYPERLINK <url> (select objects first)"); self.command_line.push_info(crate::t!("Usage: HYPERLINK <url> (select objects first)").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
let handles: Vec<acadrust::Handle> = let handles: Vec<acadrust::Handle> =
self.tabs[i].scene.selected_entities().iter().map(|(h, _)| *h).collect(); self.tabs[i].scene.selected_entities().iter().map(|(h, _)| *h).collect();
if handles.is_empty() { if handles.is_empty() {
self.command_line.push_error("HYPERLINK: select objects first."); self.command_line.push_error(crate::t!("HYPERLINK: select objects first.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
self.push_undo_snapshot(i, "HYPERLINK"); self.push_undo_snapshot(i, "HYPERLINK");
@ -710,7 +710,7 @@ impl OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("HYPERLINK: attached to {n} object(s).")); .push_output(crate::tf!("HYPERLINK: attached to {n} object(s).").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
@ -745,10 +745,10 @@ impl OpenCADStudio {
.collect(); .collect();
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_error("ADJUST: select raster image(s) first."); .push_error(crate::t!("ADJUST: select raster image(s) first.").as_ref());
} else if action.is_empty() { } else if action.is_empty() {
self.command_line self.command_line
.push_info("Usage: ADJUST BRIGHTNESS|CONTRAST|FADE <0-100>"); .push_info(crate::t!("Usage: ADJUST BRIGHTNESS|CONTRAST|FADE <0-100>").as_ref());
} else if let Ok(v) = arg.parse::<u8>() { } else if let Ok(v) = arg.parse::<u8>() {
let v = v.min(100); let v = v.min(100);
self.push_undo_snapshot(i, "ADJUST"); self.push_undo_snapshot(i, "ADJUST");
@ -792,14 +792,14 @@ impl OpenCADStudio {
.collect(); .collect();
self.tabs[i].scene.bump_entities(&changes); self.tabs[i].scene.bump_entities(&changes);
self.command_line self.command_line
.push_output(&format!("ADJUST: {action} = {v} on {changed} image(s).")); .push_output(crate::tf!("ADJUST: {action} = {v} on {changed} image(s).").as_ref());
} else { } else {
self.command_line.push_error( self.command_line.push_error(
"ADJUST: no raster images selected, or unknown property (use BRIGHTNESS|CONTRAST|FADE).", "ADJUST: no raster images selected, or unknown property (use BRIGHTNESS|CONTRAST|FADE).",
); );
} }
} else { } else {
self.command_line.push_error("ADJUST: value must be 0-100."); self.command_line.push_error(crate::t!("ADJUST: value must be 0-100.").as_ref());
} }
} }
@ -837,7 +837,7 @@ impl OpenCADStudio {
} }
_ => self _ => self
.command_line .command_line
.push_error("ANNOALLVISIBLE: enter 0 or 1."), .push_error(crate::t!("ANNOALLVISIBLE: enter 0 or 1.").as_ref()),
} }
} }
"ANNOAUTOSCALE" => { "ANNOAUTOSCALE" => {
@ -867,7 +867,7 @@ impl OpenCADStudio {
.collect(); .collect();
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_error("ANNOUPDATE: select annotation objects first."); .push_error(crate::t!("ANNOUPDATE: select annotation objects first.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
self.push_undo_snapshot(i, "ANNOUPDATE"); self.push_undo_snapshot(i, "ANNOUPDATE");
@ -891,7 +891,7 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
} }
self.command_line self.command_line
.push_output(&format!("ANNOUPDATE: updated {updated} object(s).")); .push_output(crate::tf!("ANNOUPDATE: updated {updated} object(s).").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
cmd if cmd.starts_with("ANNOSCALE ") || cmd.starts_with("CANNOSCALE ") => { cmd if cmd.starts_with("ANNOSCALE ") || cmd.starts_with("CANNOSCALE ") => {
@ -909,7 +909,7 @@ impl OpenCADStudio {
.current_annotation_scale .current_annotation_scale
.clone(); .clone();
self.command_line self.command_line
.push_output(&format!("Current annotation scale: {name}")); .push_output(crate::tf!("Current annotation scale: {name}").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
let previous = self.tabs[i].scene.displayed_annotation_scale_handle(); let previous = self.tabs[i].scene.displayed_annotation_scale_handle();
@ -924,11 +924,11 @@ impl OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("Annotation scale: {arg}")); .push_output(crate::tf!("Annotation scale: {arg}").as_ref());
} }
None => self None => self
.command_line .command_line
.push_error("Usage: ANNOSCALE <ratio> e.g. 1:50, 2:1, or a factor"), .push_error(crate::t!("Usage: ANNOSCALE <ratio> e.g. 1:50, 2:1, or a factor").as_ref()),
} }
} }
@ -962,19 +962,19 @@ impl OpenCADStudio {
if self.tabs[i].scene.add_scale(arg, paper, drawing) { if self.tabs[i].scene.add_scale(arg, paper, drawing) {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("Added annotation scale {arg}.")); .push_output(crate::tf!("Added annotation scale {arg}.").as_ref());
} else { } else {
self.command_line self.command_line
.push_info(&format!("Scale {arg} already exists.")); .push_info(crate::tf!("Scale {arg} already exists.").as_ref());
} }
} }
_ => self _ => self
.command_line .command_line
.push_error("SCALELISTEDIT ADD: use a ratio like 1:50."), .push_error(crate::t!("SCALELISTEDIT ADD: use a ratio like 1:50.").as_ref()),
}, },
None => self None => self
.command_line .command_line
.push_error("SCALELISTEDIT ADD: use a ratio like 1:50."), .push_error(crate::t!("SCALELISTEDIT ADD: use a ratio like 1:50.").as_ref()),
}, },
"DELETE" | "REMOVE" => { "DELETE" | "REMOVE" => {
let current = self.tabs[i] let current = self.tabs[i]
@ -984,20 +984,20 @@ impl OpenCADStudio {
.current_annotation_scale .current_annotation_scale
.clone(); .clone();
if arg.is_empty() { if arg.is_empty() {
self.command_line.push_info("Usage: SCALELISTEDIT DELETE <name>"); self.command_line.push_info(crate::t!("Usage: SCALELISTEDIT DELETE <name>").as_ref());
} else if arg.eq_ignore_ascii_case(&current) { } else if arg.eq_ignore_ascii_case(&current) {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"Cannot delete the current annotation scale ({arg})." "Cannot delete the current annotation scale ({arg})."
)); ).as_ref());
} else { } else {
self.push_undo_snapshot(i, "SCALELISTEDIT"); self.push_undo_snapshot(i, "SCALELISTEDIT");
if self.tabs[i].scene.remove_scale(arg) { if self.tabs[i].scene.remove_scale(arg) {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("Removed annotation scale {arg}.")); .push_output(crate::tf!("Removed annotation scale {arg}.").as_ref());
} else { } else {
self.command_line self.command_line
.push_info(&format!("No annotation scale named {arg}.")); .push_info(crate::tf!("No annotation scale named {arg}.").as_ref());
} }
} }
} }
@ -1009,15 +1009,15 @@ impl OpenCADStudio {
.map(|(n, _, _)| n) .map(|(n, _, _)| n)
.collect(); .collect();
if names.is_empty() { if names.is_empty() {
self.command_line.push_info("No annotation scales defined."); self.command_line.push_info(crate::t!("No annotation scales defined.").as_ref());
} else { } else {
self.command_line self.command_line
.push_output(&format!("Annotation scales: {}", names.join(", "))); .push_output(crate::tf!("Annotation scales: {}", names.join(", ")).as_ref());
} }
} }
_ => self _ => self
.command_line .command_line
.push_info("Usage: SCALELISTEDIT [ADD 1:50 | DELETE 1:50]"), .push_info(crate::t!("Usage: SCALELISTEDIT [ADD 1:50 | DELETE 1:50]").as_ref()),
} }
} }
@ -1056,7 +1056,7 @@ impl OpenCADStudio {
let ncols = rows_data.iter().map(|r| r.len()).max().unwrap_or(0); let ncols = rows_data.iter().map(|r| r.len()).max().unwrap_or(0);
if nrows == 0 || ncols == 0 { if nrows == 0 || ncols == 0 {
self.command_line self.command_line
.push_error("DATALINK: the CSV file is empty."); .push_error(crate::t!("DATALINK: the CSV file is empty.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
use acadrust::entities::TableBuilder; use acadrust::entities::TableBuilder;
@ -1076,13 +1076,13 @@ impl OpenCADStudio {
.scene .scene
.add_entity_clone(acadrust::EntityType::Table(table)); .add_entity_clone(acadrust::EntityType::Table(table));
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"DATALINK: imported {nrows}×{ncols} cells into a table at the origin." "DATALINK: imported {nrows}×{ncols} cells into a table at the origin."
)); ).as_ref());
} }
Err(e) => { Err(e) => {
self.command_line self.command_line
.push_error(&format!("DATALINK: cannot read \"{path}\": {e}")); .push_error(crate::tf!("DATALINK: cannot read \"{path}\": {e}").as_ref());
} }
} }
} }
@ -1110,7 +1110,7 @@ impl OpenCADStudio {
let pts = parse_landxml_cgpoints(&xml); let pts = parse_landxml_cgpoints(&xml);
if pts.is_empty() { if pts.is_empty() {
self.command_line self.command_line
.push_info("LANDXMLIMPORT: no <CgPoint> survey points found."); .push_info(crate::t!("LANDXMLIMPORT: no <CgPoint> survey points found.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
self.push_undo_snapshot(i, "LANDXMLIMPORT"); self.push_undo_snapshot(i, "LANDXMLIMPORT");
@ -1122,21 +1122,21 @@ impl OpenCADStudio {
.add_entity_clone(acadrust::EntityType::Point(p)); .add_entity_clone(acadrust::EntityType::Point(p));
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"LANDXMLIMPORT: imported {} survey point(s). Use ZOOM EXTENTS to view.", "LANDXMLIMPORT: imported {} survey point(s). Use ZOOM EXTENTS to view.",
pts.len() pts.len()
)); ).as_ref());
} }
Err(e) => self Err(e) => self
.command_line .command_line
.push_error(&format!("LANDXMLIMPORT: cannot read \"{path}\": {e}")), .push_error(crate::tf!("LANDXMLIMPORT: cannot read \"{path}\": {e}").as_ref()),
} }
} }
"POINTCLOUDATTACH" | "RECAP" | "SYNCPVIEWPORTS" | "UNDERLAYLAYERS" "POINTCLOUDATTACH" | "RECAP" | "SYNCPVIEWPORTS" | "UNDERLAYLAYERS"
| "UOSNAP" => { | "UOSNAP" => {
self.command_line self.command_line
.push_info(&format!("{cmd}: not yet implemented.")); .push_info(crate::tf!("{cmd}: not yet implemented.").as_ref());
} }
_ => return None, _ => return None,

View file

@ -76,7 +76,7 @@ impl OpenCADStudio {
.collect(); .collect();
if selected_handles.is_empty() { if selected_handles.is_empty() {
self.command_line self.command_line
.push_error("ATTEDIT: select an Insert entity first."); .push_error(crate::t!("ATTEDIT: select an Insert entity first.").as_ref());
} else { } else {
let mut found_any = false; let mut found_any = false;
for sh in &selected_handles { for sh in &selected_handles {
@ -90,17 +90,17 @@ impl OpenCADStudio {
if rest.is_empty() { if rest.is_empty() {
// List attributes. // List attributes.
if ins.attributes.is_empty() { if ins.attributes.is_empty() {
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" Insert {:x}: no attributes.", " Insert {:x}: no attributes.",
sh.value() sh.value()
)); ).as_ref());
} else { } else {
for attr in &ins.attributes { for attr in &ins.attributes {
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" [{tag}] = {val}", " [{tag}] = {val}",
tag = attr.tag, tag = attr.tag,
val = attr.get_value() val = attr.get_value()
)); ).as_ref());
} }
} }
} }
@ -108,7 +108,7 @@ impl OpenCADStudio {
} }
if !found_any { if !found_any {
self.command_line self.command_line
.push_error("ATTEDIT: no Insert entities in selection."); .push_error(crate::t!("ATTEDIT: no Insert entities in selection.").as_ref());
} }
// If tag + value supplied, mutate attributes. // If tag + value supplied, mutate attributes.
if parts.len() == 2 && !parts[0].is_empty() { if parts.len() == 2 && !parts[0].is_empty() {
@ -133,13 +133,13 @@ impl OpenCADStudio {
} }
if changed > 0 { if changed > 0 {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"ATTEDIT: updated {changed} attribute(s) [{tag_up}] = {new_val}." "ATTEDIT: updated {changed} attribute(s) [{tag_up}] = {new_val}."
)); ).as_ref());
} else { } else {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"ATTEDIT: tag '{tag_up}' not found in selection." "ATTEDIT: tag '{tag_up}' not found in selection."
)); ).as_ref());
} }
} }
} }
@ -187,13 +187,13 @@ impl OpenCADStudio {
} }
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"ATTDISP {sub}: {count} attribute definition(s) updated." "ATTDISP {sub}: {count} attribute definition(s) updated."
)); ).as_ref());
} }
_ => { _ => {
self.command_line self.command_line
.push_info("Usage: ATTDISP ON | OFF | NORMAL"); .push_info(crate::t!("Usage: ATTDISP ON | OFF | NORMAL").as_ref());
} }
} }
} }
@ -327,7 +327,7 @@ impl OpenCADStudio {
} }
None => { None => {
self.command_line self.command_line
.push_info("ARC Continue No previous line or arc to continue."); .push_info(crate::t!("ARC Continue No previous line or arc to continue.").as_ref());
} }
} }
} }
@ -599,7 +599,7 @@ impl OpenCADStudio {
self.tabs[i].active_cmd = Some(Box::new(cmd)); self.tabs[i].active_cmd = Some(Box::new(cmd));
} else { } else {
self.command_line self.command_line
.push_error("HATCHEDIT: selected entity is not a hatch."); .push_error(crate::t!("HATCHEDIT: selected entity is not a hatch.").as_ref());
} }
} else { } else {
let cmd = HatcheditCommand::new(); let cmd = HatcheditCommand::new();
@ -721,7 +721,7 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
self.command_line self.command_line
.push_output(&format!("{n} object(s) erased.")); .push_output(crate::tf!("{n} object(s) erased.").as_ref());
if let Some(pd) = pending { if let Some(pd) = pending {
self.commit_undo_delta(i, pd); self.commit_undo_delta(i, pd);
} }
@ -783,7 +783,7 @@ impl OpenCADStudio {
return Some(self.solid_polysolid(nums[0], nums[1])); return Some(self.solid_polysolid(nums[0], nums[1]));
} }
self.command_line self.command_line
.push_info("Usage: POLYSOLID <width> <height> (select a polyline first)"); .push_info(crate::t!("Usage: POLYSOLID <width> <height> (select a polyline first)").as_ref());
} }
// SPLINEFIT — fit a smooth spline through the selected polyline's points. // SPLINEFIT — fit a smooth spline through the selected polyline's points.
@ -829,7 +829,7 @@ impl OpenCADStudio {
} }
if loops.is_empty() { if loops.is_empty() {
self.command_line self.command_line
.push_error("REGION: select closed polylines or circles."); .push_error(crate::t!("REGION: select closed polylines or circles.").as_ref());
} else { } else {
self.push_undo_snapshot(i, "REGION"); self.push_undo_snapshot(i, "REGION");
let count = loops.len(); let count = loops.len();
@ -845,7 +845,7 @@ impl OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("REGION: created {count} region(s).")); .push_output(crate::tf!("REGION: created {count} region(s).").as_ref());
} }
} }
@ -871,7 +871,7 @@ impl OpenCADStudio {
return Some(self.solid_pyramid(nums[0], nums[1], sides)); return Some(self.solid_pyramid(nums[0], nums[1], sides));
} }
self.command_line self.command_line
.push_info("Usage: PYRAMID <radius> <height> [sides] (default 4 sides)"); .push_info(crate::t!("Usage: PYRAMID <radius> <height> [sides] (default 4 sides)").as_ref());
} }
// SECTION [X|Y|Z] <value> — draw the cross-section outline of the solid. // SECTION [X|Y|Z] <value> — draw the cross-section outline of the solid.
@ -1070,7 +1070,7 @@ impl OpenCADStudio {
} }
if sel.len() == 1 { if sel.len() == 1 {
self.command_line self.command_line
.push_error("DDEDIT: selected entity is not text."); .push_error(crate::t!("DDEDIT: selected entity is not text.").as_ref());
} else { } else {
let cmd = DdeditCommand::new(); let cmd = DdeditCommand::new();
self.command_line.push_info(&cmd.prompt()); self.command_line.push_info(&cmd.prompt());
@ -1093,7 +1093,7 @@ impl OpenCADStudio {
"Multiple" "Multiple"
}; };
self.command_line self.command_line
.push_output(&format!("Current settings: Edit mode = {}", mode_str)); .push_output(crate::tf!("Current settings: Edit mode = {}", mode_str).as_ref());
let new_cmd = TexteditCommand::new(self.texteditmode); let new_cmd = TexteditCommand::new(self.texteditmode);
self.command_line.push_info(&new_cmd.prompt()); self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd)); self.tabs[i].active_cmd = Some(Box::new(new_cmd));

View file

@ -11,7 +11,7 @@ impl OpenCADStudio {
"SAVEALL" => { "SAVEALL" => {
if self.read_only { if self.read_only {
self.command_line self.command_line
.push_error("Read-only session (--read-only): saving is disabled."); .push_error(crate::t!("Read-only session (--read-only): saving is disabled.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -27,10 +27,10 @@ impl OpenCADStudio {
.active_save_jobs .active_save_jobs
.contains_key(&self.tabs[t].id) .contains_key(&self.tabs[t].id)
{ {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"SAVEALL: {} already has a save running", "SAVEALL: {} already has a save running",
path.display() path.display()
)); ).as_ref());
skipped += 1; skipped += 1;
continue; continue;
} }
@ -42,28 +42,28 @@ impl OpenCADStudio {
Ok(()) => { Ok(()) => {
saved += 1; saved += 1;
} }
Err(e) => self.command_line.push_error(&format!( Err(e) => self.command_line.push_error(crate::tf!(
"SAVEALL: {} failed: {e}", "SAVEALL: {} failed: {e}",
path.display() path.display()
)), ).as_ref()),
} }
} else { } else {
skipped += 1; skipped += 1;
} }
} }
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"SAVEALL: saved {saved} drawing(s){}.", "SAVEALL: saved {saved} drawing(s){}.",
if skipped > 0 { if skipped > 0 {
format!("; {skipped} need SAVEAS (no file path yet)") format!("; {skipped} need SAVEAS (no file path yet)")
} else { } else {
String::new() String::new()
} }
)); ).as_ref());
} }
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
self.command_line self.command_line
.push_info("SAVEALL: save each tab individually in the web build."); .push_info(crate::t!("SAVEALL: save each tab individually in the web build.").as_ref());
} }
return Some(Task::none()); return Some(Task::none());
} }
@ -76,7 +76,7 @@ impl OpenCADStudio {
Ok(n) => return Some(Task::done(Message::UndoMany(n))), Ok(n) => return Some(Task::done(Message::UndoMany(n))),
Err(_) => { Err(_) => {
self.command_line self.command_line
.push_error("Usage: UNDO [number of steps]"); .push_error(crate::t!("Usage: UNDO [number of steps]").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
} }
@ -87,7 +87,7 @@ impl OpenCADStudio {
// without undoing any work done since. // without undoing any work done since.
"OOPS" => { "OOPS" => {
if self.oops_cache.is_empty() { if self.oops_cache.is_empty() {
self.command_line.push_info("OOPS: nothing to restore."); self.command_line.push_info(crate::t!("OOPS: nothing to restore.").as_ref());
} else { } else {
let restored = std::mem::take(&mut self.oops_cache); let restored = std::mem::take(&mut self.oops_cache);
let pending = self.begin_undo(i, "OOPS", restored.len(), true); let pending = self.begin_undo(i, "OOPS", restored.len(), true);
@ -99,7 +99,7 @@ impl OpenCADStudio {
self.commit_undo_delta(i, pending); self.commit_undo_delta(i, pending);
} }
self.command_line self.command_line
.push_output(&format!("OOPS: restored {n} object(s).")); .push_output(crate::tf!("OOPS: restored {n} object(s).").as_ref());
} }
} }
"CLEAR" | "CLR" => return Some(Task::done(Message::ClearScene)), "CLEAR" | "CLR" => return Some(Task::done(Message::ClearScene)),
@ -161,7 +161,7 @@ impl OpenCADStudio {
} }
}; };
self.command_line self.command_line
.push_output(&format!("Visual style: {label}.")); .push_output(crate::tf!("Visual style: {label}.").as_ref());
return Some(Task::done(Message::SetWireframe(wireframe))); return Some(Task::done(Message::SetWireframe(wireframe)));
} }
// CLOSE — close the active drawing tab (with the unsaved-changes // CLOSE — close the active drawing tab (with the unsaved-changes
@ -216,18 +216,18 @@ impl OpenCADStudio {
} }
} }
} }
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"{cmd}: packaged {copied} file(s) into {}", "{cmd}: packaged {copied} file(s) into {}",
folder.display() folder.display()
)); ).as_ref());
} }
Err(e) => self Err(e) => self
.command_line .command_line
.push_error(&format!("{cmd}: cannot create folder ({e}).")), .push_error(crate::tf!("{cmd}: cannot create folder ({e}).").as_ref()),
} }
} else { } else {
self.command_line self.command_line
.push_error("ARCHIVE: save the drawing first (it has no file path yet)."); .push_error(crate::t!("ARCHIVE: save the drawing first (it has no file path yet).").as_ref());
} }
} }
@ -302,7 +302,7 @@ impl OpenCADStudio {
self.tabs[i].scene.recolor_meshes(); self.tabs[i].scene.recolor_meshes();
self.tabs[i].scene.bump_geometry(); self.tabs[i].scene.bump_geometry();
self.command_line self.command_line
.push_output("Background reset to default."); .push_output(crate::t!("Background reset to default.").as_ref());
} else if let Some(rgba) = parse_background_color(&args) { } else if let Some(rgba) = parse_background_color(&args) {
if is_paper { if is_paper {
self.tabs[i].paper_bg_color = Some(rgba); self.tabs[i].paper_bg_color = Some(rgba);
@ -316,12 +316,12 @@ impl OpenCADStudio {
self.tabs[i].scene.recolor_meshes(); self.tabs[i].scene.recolor_meshes();
self.tabs[i].scene.bump_geometry(); self.tabs[i].scene.bump_geometry();
let [r, g, b, _] = rgba; let [r, g, b, _] = rgba;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Background: rgb({}, {}, {})", "Background: rgb({}, {}, {})",
(r * 255.0).round() as u8, (r * 255.0).round() as u8,
(g * 255.0).round() as u8, (g * 255.0).round() as u8,
(b * 255.0).round() as u8 (b * 255.0).round() as u8
)); ).as_ref());
// Persisted centrally after this message via // Persisted centrally after this message via
// `persist_settings_if_changed()`. // `persist_settings_if_changed()`.
} else { } else {
@ -361,20 +361,20 @@ impl OpenCADStudio {
}) })
.map(|l| Task::done(Message::Command(l.to_string()))) .map(|l| Task::done(Message::Command(l.to_string())))
.collect(); .collect();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"SCRIPT: running {} command(s) from {p}.", "SCRIPT: running {} command(s) from {p}.",
cmds.len() cmds.len()
)); ).as_ref());
return Some(Task::batch(cmds)); return Some(Task::batch(cmds));
} }
Err(e) => { Err(e) => {
self.command_line self.command_line
.push_error(&format!("SCRIPT: cannot read {p}: {e}")); .push_error(crate::tf!("SCRIPT: cannot read {p}: {e}").as_ref());
} }
}, },
_ => { _ => {
self.command_line self.command_line
.push_info("Usage: SCRIPT <path to .scr file>"); .push_info(crate::t!("Usage: SCRIPT <path to .scr file>").as_ref());
} }
} }
} }

View file

@ -5,7 +5,7 @@ impl OpenCADStudio {
match cmd { match cmd {
"3DORBIT" => { "3DORBIT" => {
self.command_line self.command_line
.push_info("3D Orbit: drag with right mouse button."); .push_info(crate::t!("3D Orbit: drag with right mouse button.").as_ref());
} }
// ── Selection utilities ─────────────────────────────────────── // ── Selection utilities ───────────────────────────────────────
@ -24,20 +24,20 @@ impl OpenCADStudio {
self.tabs[i].scene.select_entity(h, false); self.tabs[i].scene.select_entity(h, false);
} }
self.command_line self.command_line
.push_output(&format!("SELECTALL: {} object(s) selected.", count)); .push_output(crate::tf!("SELECTALL: {} object(s) selected.", count).as_ref());
self.refresh_properties(); self.refresh_properties();
} }
"DESELECT" | "DESELALL" => { "DESELECT" | "DESELALL" => {
self.tabs[i].scene.deselect_all(); self.tabs[i].scene.deselect_all();
self.command_line.push_output("Deselected."); self.command_line.push_output(crate::t!("Deselected.").as_ref());
self.refresh_properties(); self.refresh_properties();
} }
"SELECTSIMILAR" | "SELSIM" => { "SELECTSIMILAR" | "SELSIM" => {
let added = self.tabs[i].scene.select_similar(); let added = self.tabs[i].scene.select_similar();
self.command_line self.command_line
.push_output(&format!("Select Similar: {} added.", added)); .push_output(crate::tf!("Select Similar: {} added.", added).as_ref());
self.refresh_properties(); self.refresh_properties();
} }
@ -68,11 +68,11 @@ impl OpenCADStudio {
let expr = cmd.splitn(2, char::is_whitespace).nth(1).unwrap_or("").trim(); let expr = cmd.splitn(2, char::is_whitespace).nth(1).unwrap_or("").trim();
if expr.is_empty() { if expr.is_empty() {
self.command_line self.command_line
.push_info("Usage: CAL <expression> e.g. CAL (2+3)*4"); .push_info(crate::t!("Usage: CAL <expression> e.g. CAL (2+3)*4").as_ref());
} else { } else {
match arith_eval(expr) { match arith_eval(expr) {
Ok(v) => self.command_line.push_output(&format!("= {v}")), Ok(v) => self.command_line.push_output(crate::tf!("= {v}").as_ref()),
Err(e) => self.command_line.push_error(&format!("CAL: {e}")), Err(e) => self.command_line.push_error(crate::tf!("CAL: {e}").as_ref()),
} }
} }
} }
@ -82,7 +82,7 @@ impl OpenCADStudio {
let selected: Vec<_> = self.tabs[i].scene.selected_entities(); let selected: Vec<_> = self.tabs[i].scene.selected_entities();
if selected.is_empty() { if selected.is_empty() {
self.command_line self.command_line
.push_error("LIST: no entities selected. Select entities first."); .push_error(crate::t!("LIST: no entities selected. Select entities first.").as_ref());
} else { } else {
for (handle, _) in &selected { for (handle, _) in &selected {
if let Some(entity) = self.tabs[i].scene.document.get_entity(*handle) { if let Some(entity) = self.tabs[i].scene.document.get_entity(*handle) {
@ -101,7 +101,7 @@ impl OpenCADStudio {
}; };
// Entity-specific details // Entity-specific details
let details = entity_list_details(entity); let details = entity_list_details(entity);
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"{type_name} Handle:{:X} Layer:{} Color:{} LT:{}{}", "{type_name} Handle:{:X} Layer:{} Color:{} LT:{}{}",
handle.value(), handle.value(),
common.layer, common.layer,
@ -112,7 +112,7 @@ impl OpenCADStudio {
} else { } else {
format!("\n {details}") format!("\n {details}")
} }
)); ).as_ref());
} }
} }
} }
@ -127,7 +127,7 @@ impl OpenCADStudio {
let selected = scene.selected_entities(); let selected = scene.selected_entities();
if selected.is_empty() { if selected.is_empty() {
self.command_line self.command_line
.push_error("SELHANDLES: no entities selected. Select entities first."); .push_error(crate::t!("SELHANDLES: no entities selected. Select entities first.").as_ref());
} else { } else {
use std::collections::BTreeMap; use std::collections::BTreeMap;
let space = if scene.current_layout == "Model" { let space = if scene.current_layout == "Model" {
@ -207,16 +207,14 @@ impl OpenCADStudio {
.collect(); .collect();
if lines.is_empty() { if lines.is_empty() {
self.command_line self.command_line
.push_info("DBLIST: drawing has no entities."); .push_info(crate::t!("DBLIST: drawing has no entities.").as_ref());
} else { } else {
let count = lines.len(); let count = lines.len();
for l in lines { for l in lines {
self.command_line.push_output(&l); self.command_line.push_output(&l);
} }
self.command_line.push_output(&format!( self.command_line
"DBLIST: {count} entit{}.", .push_output(crate::tf!("DBLIST: {count} objects.").as_ref());
if count == 1 { "y" } else { "ies" }
));
} }
} }
@ -311,7 +309,7 @@ impl OpenCADStudio {
// If a session is already active, tell the user. // If a session is already active, tell the user.
if self.tabs[i].refedit_session.is_some() { if self.tabs[i].refedit_session.is_some() {
self.command_line self.command_line
.push_error("REFEDIT: a session is already active. Use REFCLOSE first."); .push_error(crate::t!("REFEDIT: a session is already active. Use REFCLOSE first.").as_ref());
} else { } else {
// Check if a single INSERT is already selected. // Check if a single INSERT is already selected.
let selected: Vec<_> = let selected: Vec<_> =
@ -337,7 +335,7 @@ impl OpenCADStudio {
use crate::modules::draw::modify::block_edit::BlockEditPickCommand; use crate::modules::draw::modify::block_edit::BlockEditPickCommand;
if self.tabs[i].refedit_session.is_some() { if self.tabs[i].refedit_session.is_some() {
self.command_line self.command_line
.push_error("BEDIT: finish the active REFEDIT (REFCLOSE) first."); .push_error(crate::t!("BEDIT: finish the active REFEDIT (REFCLOSE) first.").as_ref());
} else { } else {
// Jump straight to begin when a single INSERT is preselected. // Jump straight to begin when a single INSERT is preselected.
let selected: Vec<_> = let selected: Vec<_> =
@ -369,7 +367,7 @@ impl OpenCADStudio {
Some(acadrust::EntityType::Insert(ins)) => ins.clone(), Some(acadrust::EntityType::Insert(ins)) => ins.clone(),
_ => { _ => {
self.command_line self.command_line
.push_error("BEDIT: selected object is not a block reference."); .push_error(crate::t!("BEDIT: selected object is not a block reference.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
}; };
@ -383,16 +381,16 @@ impl OpenCADStudio {
{ {
Some(br) => (br.handle, br.flags.is_xref), Some(br) => (br.handle, br.flags.is_xref),
None => { None => {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"BEDIT: block \"{}\" not found.", "BEDIT: block \"{}\" not found.",
insert.block_name insert.block_name
)); ).as_ref());
return Some(Task::none()); return Some(Task::none());
} }
}; };
if is_xref { if is_xref {
self.command_line self.command_line
.push_error("BEDIT: cannot edit an external reference (xref)."); .push_error(crate::t!("BEDIT: cannot edit an external reference (xref).").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
if self.tabs[i] if self.tabs[i]
@ -519,10 +517,10 @@ impl OpenCADStudio {
self.refresh_properties(); self.refresh_properties();
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"BEDIT: Editing block \"{}\". Use Save Block or Discard to finish.", "BEDIT: Editing block \"{}\". Use Save Block or Discard to finish.",
insert.block_name insert.block_name
)); ).as_ref());
} }
"BEDIT_SAVE" => { "BEDIT_SAVE" => {
@ -532,12 +530,12 @@ impl OpenCADStudio {
} }
None => { None => {
self.command_line self.command_line
.push_error("BEDIT_SAVE: no block editor is open."); .push_error(crate::t!("BEDIT_SAVE: no block editor is open.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
Some(_) => { Some(_) => {
self.command_line self.command_line
.push_error("BEDIT_SAVE: invalid block editor state."); .push_error(crate::t!("BEDIT_SAVE: invalid block editor state.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
}; };
@ -549,10 +547,10 @@ impl OpenCADStudio {
&session.return_camera, &session.return_camera,
); );
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"BEDIT: Block \"{}\" saved. All references updated.", "BEDIT: Block \"{}\" saved. All references updated.",
session.block_name session.block_name
)); ).as_ref());
} }
"BEDIT_DISCARD" => { "BEDIT_DISCARD" => {
@ -562,12 +560,12 @@ impl OpenCADStudio {
} }
None => { None => {
self.command_line self.command_line
.push_error("BEDIT_DISCARD: no block editor is open."); .push_error(crate::t!("BEDIT_DISCARD: no block editor is open.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
Some(_) => { Some(_) => {
self.command_line self.command_line
.push_error("BEDIT_DISCARD: invalid block editor state."); .push_error(crate::t!("BEDIT_DISCARD: invalid block editor state.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
}; };
@ -625,10 +623,10 @@ impl OpenCADStudio {
&return_camera, &return_camera,
); );
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"BEDIT: Block \"{}\" edit discarded.", "BEDIT: Block \"{}\" edit discarded.",
block_name block_name
)); ).as_ref());
} }
cmd if cmd.starts_with("REFEDIT_BEGIN:") => { cmd if cmd.starts_with("REFEDIT_BEGIN:") => {
@ -645,7 +643,7 @@ impl OpenCADStudio {
Some(acadrust::EntityType::Insert(ins)) => ins.clone(), Some(acadrust::EntityType::Insert(ins)) => ins.clone(),
_ => { _ => {
self.command_line self.command_line
.push_error("REFEDIT: selected object is not an INSERT."); .push_error(crate::t!("REFEDIT: selected object is not an INSERT.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
}; };
@ -681,10 +679,10 @@ impl OpenCADStudio {
{ {
Some(br) => br.handle, Some(br) => br.handle,
None => { None => {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"REFEDIT: block \"{}\" not found.", "REFEDIT: block \"{}\" not found.",
insert.block_name insert.block_name
)); ).as_ref());
return Some(Task::none()); return Some(Task::none());
} }
}; };
@ -712,7 +710,7 @@ impl OpenCADStudio {
}; };
if block_entities.is_empty() { if block_entities.is_empty() {
self.command_line.push_error("REFEDIT: block is empty."); self.command_line.push_error(crate::t!("REFEDIT: block is empty.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
@ -762,10 +760,10 @@ impl OpenCADStudio {
// No active command — the user edits the block's geometry freely // No active command — the user edits the block's geometry freely
// (move, grips, draw, erase…) and runs REFCLOSE when done. (#136) // (move, grips, draw, erase…) and runs REFCLOSE when done. (#136)
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"REFEDIT: Editing block \"{}\". Run REFCLOSE to save, REFCLOSE_DISCARD to cancel.", "REFEDIT: Editing block \"{}\". Run REFCLOSE to save, REFCLOSE_DISCARD to cancel.",
insert.block_name insert.block_name
)); ).as_ref());
} }
"REFCLOSE" => { "REFCLOSE" => {
@ -776,7 +774,7 @@ impl OpenCADStudio {
self.tabs[i].active_cmd = Some(Box::new(cmd_obj)); self.tabs[i].active_cmd = Some(Box::new(cmd_obj));
} else { } else {
self.command_line self.command_line
.push_error("REFCLOSE: no REFEDIT session active."); .push_error(crate::t!("REFCLOSE: no REFEDIT session active.").as_ref());
} }
} }
@ -788,7 +786,7 @@ impl OpenCADStudio {
Some(s) => s, Some(s) => s,
None => { None => {
self.command_line self.command_line
.push_error("REFCLOSE: no REFEDIT session active."); .push_error(crate::t!("REFCLOSE: no REFEDIT session active.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
}; };
@ -866,10 +864,10 @@ impl OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"REFCLOSE: Block \"{}\" saved. All references updated.", "REFCLOSE: Block \"{}\" saved. All references updated.",
session.block_name session.block_name
)); ).as_ref());
// End the edit fade before rebuilding, so the restored geometry // End the edit fade before rebuilding, so the restored geometry
// recolours bright. (#136) // recolours bright. (#136)
self.tabs[i].scene.set_refedit_keep(None); self.tabs[i].scene.set_refedit_keep(None);
@ -882,7 +880,7 @@ impl OpenCADStudio {
Some(s) => s, Some(s) => s,
None => { None => {
self.command_line self.command_line
.push_error("REFCLOSE: no REFEDIT session active."); .push_error(crate::t!("REFCLOSE: no REFEDIT session active.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
}; };
@ -908,7 +906,7 @@ impl OpenCADStudio {
// End the edit fade — restore the drawing to full brightness. // End the edit fade — restore the drawing to full brightness.
self.tabs[i].scene.set_refedit_keep(None); self.tabs[i].scene.set_refedit_keep(None);
self.command_line self.command_line
.push_output("REFCLOSE: Changes discarded."); .push_output(crate::t!("REFCLOSE: Changes discarded.").as_ref());
} }
"ALIGN" => { "ALIGN" => {
@ -966,20 +964,20 @@ impl OpenCADStudio {
let selected = self.tabs[i].scene.selected_entities(); let selected = self.tabs[i].scene.selected_entities();
if selected.is_empty() { if selected.is_empty() {
self.command_line self.command_line
.push_error("MASSPROP: no entities selected. Select entities first."); .push_error(crate::t!("MASSPROP: no entities selected. Select entities first.").as_ref());
} else { } else {
for (handle, _) in &selected { for (handle, _) in &selected {
if let Some(entity) = self.tabs[i].scene.document.get_entity(*handle) { if let Some(entity) = self.tabs[i].scene.document.get_entity(*handle) {
use crate::entities::traits::EntityTypeOps; use crate::entities::traits::EntityTypeOps;
if let Some(props) = entity.mass_props() { if let Some(props) = entity.mass_props() {
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"{} Area={:.4} Perimeter={:.4} Centroid=({:.4},{:.4})", "{} Area={:.4} Perimeter={:.4} Centroid=({:.4},{:.4})",
crate::entities::names::dxf_name(entity), crate::entities::names::dxf_name(entity),
props.area, props.area,
props.perimeter, props.perimeter,
props.cx, props.cx,
props.cy, props.cy,
)); ).as_ref());
} }
} }
} }
@ -1003,7 +1001,7 @@ impl OpenCADStudio {
} }
}; };
if handles.is_empty() { if handles.is_empty() {
self.command_line.push_error("FLATTEN: no entities."); self.command_line.push_error(crate::t!("FLATTEN: no entities.").as_ref());
} else { } else {
self.push_undo_snapshot(i, "FLATTEN"); self.push_undo_snapshot(i, "FLATTEN");
for h in &handles { for h in &handles {
@ -1012,10 +1010,10 @@ impl OpenCADStudio {
} }
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"FLATTEN: {} entity(ies) moved to Z=0.", "FLATTEN: {} entity(ies) moved to Z=0.",
handles.len() handles.len()
)); ).as_ref());
self.refresh_properties(); self.refresh_properties();
} }
} }
@ -1054,17 +1052,17 @@ impl OpenCADStudio {
if prop.is_empty() { if prop.is_empty() {
self.command_line self.command_line
.push_info("Usage: QSELECT TYPE|LAYER|COLOR|LINETYPE <value>"); .push_info(crate::t!("Usage: QSELECT TYPE|LAYER|COLOR|LINETYPE <value>").as_ref());
} else if matched.is_empty() { } else if matched.is_empty() {
self.command_line self.command_line
.push_output("QSELECT: no matching entities."); .push_output(crate::t!("QSELECT: no matching entities.").as_ref());
} else { } else {
self.tabs[i].scene.deselect_all(); self.tabs[i].scene.deselect_all();
for h in &matched { for h in &matched {
self.tabs[i].scene.select_entity(*h, false); self.tabs[i].scene.select_entity(*h, false);
} }
self.command_line self.command_line
.push_output(&format!("QSELECT: {} entity(ies) selected.", matched.len())); .push_output(crate::tf!("QSELECT: {} entity(ies) selected.", matched.len()).as_ref());
self.refresh_properties(); self.refresh_properties();
} }
} }
@ -1102,10 +1100,10 @@ impl OpenCADStudio {
} }
let total: usize = counts.values().sum(); let total: usize = counts.values().sum();
for (k, n) in &counts { for (k, n) in &counts {
self.command_line.push_output(&format!(" {k}: {n}")); self.command_line.push_output(crate::tf!(" {k}: {n}").as_ref());
} }
self.command_line self.command_line
.push_output(&format!("COUNT: {total} entity(ies) total.")); .push_output(crate::tf!("COUNT: {total} entity(ies) total.").as_ref());
} }
"DATAEXTRACTION" | "EATTEXT" | "ATTEXT" => { "DATAEXTRACTION" | "EATTEXT" | "ATTEXT" => {
@ -1142,7 +1140,7 @@ impl OpenCADStudio {
}; };
if search.is_empty() { if search.is_empty() {
self.command_line.push_error("FIND: specify search text."); self.command_line.push_error(crate::t!("FIND: specify search text.").as_ref());
} else { } else {
let search_lc = search.to_lowercase(); let search_lc = search.to_lowercase();
let mut count = 0usize; let mut count = 0usize;
@ -1170,7 +1168,7 @@ impl OpenCADStudio {
}; };
if targets.is_empty() { if targets.is_empty() {
self.command_line self.command_line
.push_output(&format!("FIND: \"{}\" not found.", search)); .push_output(crate::tf!("FIND: \"{}\" not found.", search).as_ref());
} else { } else {
self.push_undo_snapshot(i, "FIND/REPLACE"); self.push_undo_snapshot(i, "FIND/REPLACE");
for h in &targets { for h in &targets {
@ -1182,34 +1180,34 @@ impl OpenCADStudio {
} }
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"FIND/REPLACE: replaced {} occurrence(s) of \"{}\" → \"{}\".", "FIND/REPLACE: replaced {} occurrence(s) of \"{}\" → \"{}\".",
count, search, rep count, search, rep
)); ).as_ref());
self.refresh_properties(); self.refresh_properties();
} }
} else { } else {
// List mode // List mode
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_output(&format!("FIND: \"{}\" not found.", search)); .push_output(crate::tf!("FIND: \"{}\" not found.", search).as_ref());
} else { } else {
for h in &handles { for h in &handles {
if let Some(e) = self.tabs[i].scene.document.get_entity(*h) { if let Some(e) = self.tabs[i].scene.document.get_entity(*h) {
use crate::entities::traits::EntityTypeOps; use crate::entities::traits::EntityTypeOps;
let txt = e.text_content().unwrap_or_default(); let txt = e.text_content().unwrap_or_default();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" Handle {:X}: \"{}\"", " Handle {:X}: \"{}\"",
h.value(), h.value(),
txt txt
)); ).as_ref());
} }
} }
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"FIND: {} match(es) for \"{}\".", "FIND: {} match(es) for \"{}\".",
handles.len(), handles.len(),
search search
)); ).as_ref());
} }
} }
} }
@ -1309,12 +1307,12 @@ impl OpenCADStudio {
let Some((verb, kind, layer, color, linetype, lt_scale, lw, template_dimstyle)) = info let Some((verb, kind, layer, color, linetype, lt_scale, lw, template_dimstyle)) = info
else { else {
self.command_line self.command_line
.push_error("ADDSELECTED: selected object not found."); .push_error(crate::t!("ADDSELECTED: selected object not found.").as_ref());
return Task::none(); return Task::none();
}; };
let Some(verb) = verb else { let Some(verb) = verb else {
self.command_line self.command_line
.push_error(&format!("ADDSELECTED: creating a new {kind} is not supported.")); .push_error(crate::tf!("ADDSELECTED: creating a new {kind} is not supported.").as_ref());
return Task::none(); return Task::none();
}; };
@ -1399,9 +1397,9 @@ impl OpenCADStudio {
self.ribbon.active_lineweight = lw; self.ribbon.active_lineweight = lw;
self.refresh_properties(); self.refresh_properties();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Add Selected: drawing a new {kind} on layer \"{layer}\"." "Add Selected: drawing a new {kind} on layer \"{layer}\"."
)); ).as_ref());
// Launch the matching draw command (installs its interactive step). // Launch the matching draw command (installs its interactive step).
self.dispatch_command(verb) self.dispatch_command(verb)
} }

View file

@ -36,15 +36,15 @@ impl OpenCADStudio {
}) })
.collect(); .collect();
self.command_line self.command_line
.push_output(&format!("Layers: {}", info.join(", "))); .push_output(crate::tf!("Layers: {}", info.join(", ")).as_ref());
} }
"NEW" | "N" => { "NEW" | "N" => {
let name = parts.get(1).map(|s| s.trim()).unwrap_or("").to_string(); let name = parts.get(1).map(|s| s.trim()).unwrap_or("").to_string();
if name.is_empty() { if name.is_empty() {
self.command_line.push_error("Usage: LAYER NEW <name>"); self.command_line.push_error(crate::t!("Usage: LAYER NEW <name>").as_ref());
} else if self.tabs[i].scene.document.layers.contains(&name) { } else if self.tabs[i].scene.document.layers.contains(&name) {
self.command_line self.command_line
.push_error(&format!("LAYER: '{}' already exists.", name)); .push_error(crate::tf!("LAYER: '{}' already exists.", name).as_ref());
} else { } else {
let mut layer = Layer::new(&name); let mut layer = Layer::new(&name);
// Allocate a unique handle so the layer survives a // Allocate a unique handle so the layer survives a
@ -54,7 +54,7 @@ impl OpenCADStudio {
self.push_undo_snapshot(i, "LAYER NEW"); self.push_undo_snapshot(i, "LAYER NEW");
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("LAYER: '{}' created.", name)); .push_output(crate::tf!("LAYER: '{}' created.", name).as_ref());
} }
} }
"ON" => { "ON" => {
@ -66,7 +66,7 @@ impl OpenCADStudio {
} }
self.push_undo_snapshot(i, "LAYER ON"); self.push_undo_snapshot(i, "LAYER ON");
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output("LAYER: layers turned on."); self.command_line.push_output(crate::t!("LAYER: layers turned on.").as_ref());
} }
"OFF" => { "OFF" => {
for name in &parts[1..] { for name in &parts[1..] {
@ -76,7 +76,7 @@ impl OpenCADStudio {
} }
self.push_undo_snapshot(i, "LAYER OFF"); self.push_undo_snapshot(i, "LAYER OFF");
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output("LAYER: layers turned off."); self.command_line.push_output(crate::t!("LAYER: layers turned off.").as_ref());
} }
"FREEZE" | "FR" => { "FREEZE" | "FR" => {
for name in &parts[1..] { for name in &parts[1..] {
@ -86,7 +86,7 @@ impl OpenCADStudio {
} }
self.push_undo_snapshot(i, "LAYER FREEZE"); self.push_undo_snapshot(i, "LAYER FREEZE");
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output("LAYER: layers frozen."); self.command_line.push_output(crate::t!("LAYER: layers frozen.").as_ref());
} }
"THAW" | "TH" => { "THAW" | "TH" => {
for name in &parts[1..] { for name in &parts[1..] {
@ -96,7 +96,7 @@ impl OpenCADStudio {
} }
self.push_undo_snapshot(i, "LAYER THAW"); self.push_undo_snapshot(i, "LAYER THAW");
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output("LAYER: layers thawed."); self.command_line.push_output(crate::t!("LAYER: layers thawed.").as_ref());
} }
"LOCK" | "LO" => { "LOCK" | "LO" => {
for name in &parts[1..] { for name in &parts[1..] {
@ -106,7 +106,7 @@ impl OpenCADStudio {
} }
self.push_undo_snapshot(i, "LAYER LOCK"); self.push_undo_snapshot(i, "LAYER LOCK");
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output("LAYER: layers locked."); self.command_line.push_output(crate::t!("LAYER: layers locked.").as_ref());
} }
"UNLOCK" | "UL" => { "UNLOCK" | "UL" => {
for name in &parts[1..] { for name in &parts[1..] {
@ -116,7 +116,7 @@ impl OpenCADStudio {
} }
self.push_undo_snapshot(i, "LAYER UNLOCK"); self.push_undo_snapshot(i, "LAYER UNLOCK");
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output("LAYER: layers unlocked."); self.command_line.push_output(crate::t!("LAYER: layers unlocked.").as_ref());
} }
"COLOR" | "C" => { "COLOR" | "C" => {
// LAYER COLOR <name> <aci_index> // LAYER COLOR <name> <aci_index>
@ -137,17 +137,17 @@ impl OpenCADStudio {
.invalidate_layer_dependencies(std::slice::from_ref( .invalidate_layer_dependencies(std::slice::from_ref(
&layer_name, &layer_name,
)); ));
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"LAYER: '{}' color set to ACI {}.", "LAYER: '{}' color set to ACI {}.",
layer_name, idx layer_name, idx
)); ).as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("LAYER: '{}' not found.", layer_name)); .push_error(crate::tf!("LAYER: '{}' not found.", layer_name).as_ref());
} }
} else { } else {
self.command_line self.command_line
.push_error("Usage: LAYER COLOR <name> <aci_index>"); .push_error(crate::t!("Usage: LAYER COLOR <name> <aci_index>").as_ref());
} }
} }
"SET" | "S" | "CURRENT" => { "SET" | "S" | "CURRENT" => {
@ -155,15 +155,15 @@ impl OpenCADStudio {
if self.tabs[i].scene.document.layers.contains(&name) { if self.tabs[i].scene.document.layers.contains(&name) {
self.tabs[i].layers.current_layer = name.clone(); self.tabs[i].layers.current_layer = name.clone();
self.command_line self.command_line
.push_output(&format!("LAYER: current layer set to '{}'.", name)); .push_output(crate::tf!("LAYER: current layer set to '{}'.", name).as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("LAYER: '{}' not found.", name)); .push_error(crate::tf!("LAYER: '{}' not found.", name).as_ref());
} }
} }
_ => { _ => {
self.command_line.push_info( self.command_line.push_info(
"Usage: LAYER LIST | NEW <name> | ON/OFF/FREEZE/THAW/LOCK/UNLOCK <name> | COLOR <name> <aci> | SET <name>" crate::t!("Usage: LAYER LIST | NEW <name> | ON/OFF/FREEZE/THAW/LOCK/UNLOCK <name> | COLOR <name> <aci> | SET <name>").as_ref()
); );
} }
} }
@ -202,22 +202,22 @@ impl OpenCADStudio {
.map(|u| u.name.clone()) .map(|u| u.name.clone())
.collect(); .collect();
if names.is_empty() { if names.is_empty() {
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Active UCS: {} | No named UCSs defined.", "Active UCS: {} | No named UCSs defined.",
active_name active_name
)); ).as_ref());
} else { } else {
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Active UCS: {} | Named: {}", "Active UCS: {} | Named: {}",
active_name, active_name,
names.join(", ") names.join(", ")
)); ).as_ref());
} }
} }
"SAVE" | "S" => { "SAVE" | "S" => {
let name = parts.get(2).map(|s| s.trim()).unwrap_or("").to_string(); let name = parts.get(2).map(|s| s.trim()).unwrap_or("").to_string();
if name.is_empty() { if name.is_empty() {
self.command_line.push_error("Usage: UCS SAVE <name>"); self.command_line.push_error(crate::t!("Usage: UCS SAVE <name>").as_ref());
} else { } else {
// Save the current active UCS under this name. // Save the current active UCS under this name.
let ucs = match &self.tabs[i].active_ucs { let ucs = match &self.tabs[i].active_ucs {
@ -231,27 +231,27 @@ impl OpenCADStudio {
self.tabs[i].scene.document.ucss.add_or_replace(ucs); self.tabs[i].scene.document.ucss.add_or_replace(ucs);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("UCS '{}' saved.", name)); .push_output(crate::tf!("UCS '{}' saved.", name).as_ref());
} }
} }
"DELETE" | "DEL" | "D" => { "DELETE" | "DEL" | "D" => {
let name = parts.get(2).map(|s| s.trim()).unwrap_or("").to_string(); let name = parts.get(2).map(|s| s.trim()).unwrap_or("").to_string();
if name.is_empty() { if name.is_empty() {
self.command_line.push_error("Usage: UCS DELETE <name>"); self.command_line.push_error(crate::t!("Usage: UCS DELETE <name>").as_ref());
} else if self.tabs[i].scene.document.ucss.remove(&name).is_some() { } else if self.tabs[i].scene.document.ucss.remove(&name).is_some() {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("UCS '{}' deleted.", name)); .push_output(crate::tf!("UCS '{}' deleted.", name).as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("UCS '{}' not found.", name)); .push_error(crate::tf!("UCS '{}' not found.", name).as_ref());
} }
} }
"W" | "WORLD" => { "W" | "WORLD" => {
self.tabs[i].active_ucs = None; self.tabs[i].active_ucs = None;
active_changed = true; active_changed = true;
self.command_line self.command_line
.push_output("UCS reset to World Coordinate System."); .push_output(crate::t!("UCS reset to World Coordinate System.").as_ref());
} }
// UCS ORIGIN x,y,z — shift the active UCS origin, keep axes // UCS ORIGIN x,y,z — shift the active UCS origin, keep axes
"ORIGIN" | "O" => { "ORIGIN" | "O" => {
@ -274,12 +274,12 @@ impl OpenCADStudio {
wcs_origin.z as f64, wcs_origin.z as f64,
); );
active_changed = true; active_changed = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"UCS origin set to ({:.4}, {:.4}, {:.4}).", "UCS origin set to ({:.4}, {:.4}, {:.4}).",
wcs_origin.x, wcs_origin.y, wcs_origin.z wcs_origin.x, wcs_origin.y, wcs_origin.z
)); ).as_ref());
} else { } else {
self.command_line.push_error("Usage: UCS ORIGIN x,y,z"); self.command_line.push_error(crate::t!("Usage: UCS ORIGIN x,y,z").as_ref());
} }
} }
// UCS Z angle — rotate active UCS around its Z axis by degrees // UCS Z angle — rotate active UCS around its Z axis by degrees
@ -318,9 +318,9 @@ impl OpenCADStudio {
self.tabs[i].active_ucs = Some(new_ucs); self.tabs[i].active_ucs = Some(new_ucs);
active_changed = true; active_changed = true;
self.command_line self.command_line
.push_output(&format!("UCS rotated {:.2}° around Z.", angle_deg)); .push_output(crate::tf!("UCS rotated {:.2}° around Z.", angle_deg).as_ref());
} else { } else {
self.command_line.push_error("Usage: UCS Z <angle_degrees>"); self.command_line.push_error(crate::t!("Usage: UCS Z <angle_degrees>").as_ref());
} }
} }
// UCS X angle — rotate around current UCS X axis // UCS X angle — rotate around current UCS X axis
@ -346,9 +346,9 @@ impl OpenCADStudio {
ucs.y_axis = Vector3::new(ny.x as f64, ny.y as f64, ny.z as f64); ucs.y_axis = Vector3::new(ny.x as f64, ny.y as f64, ny.z as f64);
active_changed = true; active_changed = true;
self.command_line self.command_line
.push_output(&format!("UCS rotated {:.2}° around X.", angle_deg)); .push_output(crate::tf!("UCS rotated {:.2}° around X.", angle_deg).as_ref());
} else { } else {
self.command_line.push_error("Usage: UCS X <angle_degrees>"); self.command_line.push_error(crate::t!("Usage: UCS X <angle_degrees>").as_ref());
} }
} }
// UCS Y angle — rotate around current UCS Y axis // UCS Y angle — rotate around current UCS Y axis
@ -374,9 +374,9 @@ impl OpenCADStudio {
ucs.x_axis = Vector3::new(nx.x as f64, nx.y as f64, nx.z as f64); ucs.x_axis = Vector3::new(nx.x as f64, nx.y as f64, nx.z as f64);
active_changed = true; active_changed = true;
self.command_line self.command_line
.push_output(&format!("UCS rotated {:.2}° around Y.", angle_deg)); .push_output(crate::tf!("UCS rotated {:.2}° around Y.", angle_deg).as_ref());
} else { } else {
self.command_line.push_error("Usage: UCS Y <angle_degrees>"); self.command_line.push_error(crate::t!("Usage: UCS Y <angle_degrees>").as_ref());
} }
} }
_ => { _ => {
@ -386,12 +386,12 @@ impl OpenCADStudio {
self.tabs[i].active_ucs = Some(named); self.tabs[i].active_ucs = Some(named);
active_changed = true; active_changed = true;
self.command_line self.command_line
.push_output(&format!("UCS '{}' activated.", name)); .push_output(crate::tf!("UCS '{}' activated.", name).as_ref());
} else { } else {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"UCS '{}' not found. Usage: UCS LIST | SAVE <name> | DELETE <name> | W | ORIGIN x,y,z | X/Y/Z <angle>", "UCS '{}' not found. Usage: UCS LIST | SAVE <name> | DELETE <name> | W | ORIGIN x,y,z | X/Y/Z <angle>",
name name
)); ).as_ref());
} }
} }
} }
@ -429,16 +429,16 @@ impl OpenCADStudio {
"" | "CURRENT" | "C" => { "" | "CURRENT" | "C" => {
let r = self.tabs[i].scene.viewcube_ucs_mat(); let r = self.tabs[i].scene.viewcube_ucs_mat();
self.plan_snap(i, r); self.plan_snap(i, r);
self.command_line.push_output("PLAN: current UCS plan view."); self.command_line.push_output(crate::t!("PLAN: current UCS plan view.").as_ref());
} }
"WORLD" | "W" => { "WORLD" | "W" => {
self.plan_snap(i, glam::Mat4::IDENTITY); self.plan_snap(i, glam::Mat4::IDENTITY);
self.command_line.push_output("PLAN: world plan view."); self.command_line.push_output(crate::t!("PLAN: world plan view.").as_ref());
} }
"UCS" | "U" => { "UCS" | "U" => {
let name = parts.get(2).map(|s| s.trim()).unwrap_or(""); let name = parts.get(2).map(|s| s.trim()).unwrap_or("");
if name.is_empty() { if name.is_empty() {
self.command_line.push_info("Usage: PLAN UCS <name>"); self.command_line.push_info(crate::t!("Usage: PLAN UCS <name>").as_ref());
} else { } else {
let ucs = self.tabs[i] let ucs = self.tabs[i]
.scene .scene
@ -452,20 +452,20 @@ impl OpenCADStudio {
let r = crate::app::helpers::UcsXform::from_ucs(&u) let r = crate::app::helpers::UcsXform::from_ucs(&u)
.rotation_mat(); .rotation_mat();
self.plan_snap(i, r); self.plan_snap(i, r);
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"PLAN: plan view of UCS '{}'.", "PLAN: plan view of UCS '{}'.",
u.name u.name
)); ).as_ref());
} }
None => self None => self
.command_line .command_line
.push_error(&format!("UCS '{name}' not found.")), .push_error(crate::tf!("UCS '{name}' not found.").as_ref()),
} }
} }
} }
_ => self _ => self
.command_line .command_line
.push_info("Usage: PLAN [CURRENT / UCS <name> / WORLD]"), .push_info(crate::t!("Usage: PLAN [CURRENT / UCS <name> / WORLD]").as_ref()),
} }
} }
@ -497,50 +497,50 @@ impl OpenCADStudio {
.map(|v| v.name.clone()) .map(|v| v.name.clone())
.collect(); .collect();
if views.is_empty() { if views.is_empty() {
self.command_line.push_output("No named views saved."); self.command_line.push_output(crate::t!("No named views saved.").as_ref());
} else { } else {
self.command_line self.command_line
.push_output(&format!("Named views: {}", views.join(", "))); .push_output(crate::tf!("Named views: {}", views.join(", ")).as_ref());
} }
} }
"SAVE" | "S" => { "SAVE" | "S" => {
let name = parts.get(2).map(|s| s.trim()).unwrap_or("").to_string(); let name = parts.get(2).map(|s| s.trim()).unwrap_or("").to_string();
if name.is_empty() { if name.is_empty() {
self.command_line.push_error("Usage: VIEW SAVE <name>"); self.command_line.push_error(crate::t!("Usage: VIEW SAVE <name>").as_ref());
} else { } else {
let new_view = self.tabs[i].scene.current_as_named_view(&name); let new_view = self.tabs[i].scene.current_as_named_view(&name);
self.tabs[i].scene.document.views.add_or_replace(new_view); self.tabs[i].scene.document.views.add_or_replace(new_view);
self.command_line self.command_line
.push_output(&format!("View '{}' saved.", name)); .push_output(crate::tf!("View '{}' saved.", name).as_ref());
} }
} }
"DELETE" | "DEL" | "D" => { "DELETE" | "DEL" | "D" => {
let name = parts.get(2).map(|s| s.trim()).unwrap_or("").to_string(); let name = parts.get(2).map(|s| s.trim()).unwrap_or("").to_string();
if name.is_empty() { if name.is_empty() {
self.command_line.push_error("Usage: VIEW DELETE <name>"); self.command_line.push_error(crate::t!("Usage: VIEW DELETE <name>").as_ref());
} else { } else {
if self.tabs[i].scene.document.views.remove(&name).is_some() { if self.tabs[i].scene.document.views.remove(&name).is_some() {
self.command_line self.command_line
.push_output(&format!("View '{}' deleted.", name)); .push_output(crate::tf!("View '{}' deleted.", name).as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("View '{}' not found.", name)); .push_error(crate::tf!("View '{}' not found.", name).as_ref());
} }
} }
} }
"RESTORE" | "R" => { "RESTORE" | "R" => {
let name = parts.get(2).map(|s| s.trim()).unwrap_or("").to_string(); let name = parts.get(2).map(|s| s.trim()).unwrap_or("").to_string();
if name.is_empty() { if name.is_empty() {
self.command_line.push_error("Usage: VIEW RESTORE <name>"); self.command_line.push_error(crate::t!("Usage: VIEW RESTORE <name>").as_ref());
} else { } else {
let found = self.tabs[i].scene.document.views.get(&name).cloned(); let found = self.tabs[i].scene.document.views.get(&name).cloned();
if let Some(v) = found { if let Some(v) = found {
self.tabs[i].scene.restore_named_view(&v); self.tabs[i].scene.restore_named_view(&v);
self.command_line self.command_line
.push_output(&format!("View '{}' restored.", v.name)); .push_output(crate::tf!("View '{}' restored.", v.name).as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("View '{}' not found.", name)); .push_error(crate::tf!("View '{}' not found.", name).as_ref());
} }
} }
} }
@ -573,10 +573,10 @@ impl OpenCADStudio {
if let Some(v) = found { if let Some(v) = found {
self.tabs[i].scene.restore_named_view(&v); self.tabs[i].scene.restore_named_view(&v);
self.command_line self.command_line
.push_output(&format!("View '{}' restored.", v.name)); .push_output(crate::tf!("View '{}' restored.", v.name).as_ref());
} else { } else {
self.command_line.push_error( self.command_line.push_error(
"Usage: VIEW LIST | VIEW SAVE <name> | VIEW RESTORE <name> | VIEW DELETE <name>" crate::t!("Usage: VIEW LIST | VIEW SAVE <name> | VIEW RESTORE <name> | VIEW DELETE <name>").as_ref()
); );
} }
} }
@ -614,16 +614,16 @@ impl OpenCADStudio {
}) })
.collect(); .collect();
if styles.is_empty() { if styles.is_empty() {
self.command_line.push_output("No table styles."); self.command_line.push_output(crate::t!("No table styles.").as_ref());
} else { } else {
self.command_line self.command_line
.push_output(&format!("TableStyles:\n {}", styles.join("\n "))); .push_output(crate::tf!("TableStyles:\n {}", styles.join("\n ")).as_ref());
} }
} }
"NEW" | "N" => { "NEW" | "N" => {
let name = parts.get(1).copied().unwrap_or("").to_string(); let name = parts.get(1).copied().unwrap_or("").to_string();
if name.is_empty() { if name.is_empty() {
self.command_line.push_error("Usage: TABLESTYLE NEW <name>"); self.command_line.push_error(crate::t!("Usage: TABLESTYLE NEW <name>").as_ref());
} else { } else {
let doc = &self.tabs[i].scene.document; let doc = &self.tabs[i].scene.document;
let exists = doc.objects.values().any(|o| { let exists = doc.objects.values().any(|o| {
@ -631,7 +631,7 @@ impl OpenCADStudio {
}); });
if exists { if exists {
self.command_line self.command_line
.push_error(&format!("TABLESTYLE: '{}' already exists.", name)); .push_error(crate::tf!("TABLESTYLE: '{}' already exists.", name).as_ref());
} else { } else {
self.push_undo_snapshot(i, "TABLESTYLE NEW"); self.push_undo_snapshot(i, "TABLESTYLE NEW");
let mut style = TableStyle::standard(); let mut style = TableStyle::standard();
@ -647,13 +647,13 @@ impl OpenCADStudio {
.insert(nh, ObjectType::TableStyle(style)); .insert(nh, ObjectType::TableStyle(style));
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("TABLESTYLE: '{}' created.", name)); .push_output(crate::tf!("TABLESTYLE: '{}' created.", name).as_ref());
} }
} }
} }
_ => { _ => {
self.command_line self.command_line
.push_error("Usage: TABLESTYLE [LIST|NEW <name>]"); .push_error(crate::t!("Usage: TABLESTYLE [LIST|NEW <name>]").as_ref());
} }
} }
} }
@ -693,16 +693,16 @@ impl OpenCADStudio {
}) })
.collect(); .collect();
if styles.is_empty() { if styles.is_empty() {
self.command_line.push_output("No multiline styles."); self.command_line.push_output(crate::t!("No multiline styles.").as_ref());
} else { } else {
self.command_line self.command_line
.push_output(&format!("MLineStyles:\n {}", styles.join("\n "))); .push_output(crate::tf!("MLineStyles:\n {}", styles.join("\n ")).as_ref());
} }
} }
"NEW" | "N" => { "NEW" | "N" => {
let name = parts.get(1).copied().unwrap_or("").to_string(); let name = parts.get(1).copied().unwrap_or("").to_string();
if name.is_empty() { if name.is_empty() {
self.command_line.push_error("Usage: MLSTYLE NEW <name>"); self.command_line.push_error(crate::t!("Usage: MLSTYLE NEW <name>").as_ref());
} else { } else {
let doc = &self.tabs[i].scene.document; let doc = &self.tabs[i].scene.document;
let exists = doc.objects.values().any(|o| { let exists = doc.objects.values().any(|o| {
@ -710,7 +710,7 @@ impl OpenCADStudio {
}); });
if exists { if exists {
self.command_line self.command_line
.push_error(&format!("MLSTYLE: '{}' already exists.", name)); .push_error(crate::tf!("MLSTYLE: '{}' already exists.", name).as_ref());
} else { } else {
self.push_undo_snapshot(i, "MLSTYLE NEW"); self.push_undo_snapshot(i, "MLSTYLE NEW");
let mut style = MLineStyle::standard(); let mut style = MLineStyle::standard();
@ -726,14 +726,14 @@ impl OpenCADStudio {
.insert(nh, ObjectType::MLineStyle(style)); .insert(nh, ObjectType::MLineStyle(style));
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("MLSTYLE: '{}' created.", name)); .push_output(crate::tf!("MLSTYLE: '{}' created.", name).as_ref());
} }
} }
} }
"SET" | "S" => { "SET" | "S" => {
let name = parts.get(1).copied().unwrap_or("").to_string(); let name = parts.get(1).copied().unwrap_or("").to_string();
if name.is_empty() { if name.is_empty() {
self.command_line.push_error("Usage: MLSTYLE SET <name>"); self.command_line.push_error(crate::t!("Usage: MLSTYLE SET <name>").as_ref());
} else { } else {
let doc = &self.tabs[i].scene.document; let doc = &self.tabs[i].scene.document;
let exists = doc.objects.values().any(|o| { let exists = doc.objects.values().any(|o| {
@ -743,13 +743,13 @@ impl OpenCADStudio {
self.push_undo_snapshot(i, "MLSTYLE SET"); self.push_undo_snapshot(i, "MLSTYLE SET");
self.tabs[i].scene.document.header.multiline_style = name.clone(); self.tabs[i].scene.document.header.multiline_style = name.clone();
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"MLSTYLE: current style set to '{}'.", "MLSTYLE: current style set to '{}'.",
name name
)); ).as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("MLSTYLE: '{}' not found.", name)); .push_error(crate::tf!("MLSTYLE: '{}' not found.", name).as_ref());
} }
} }
} }
@ -757,7 +757,7 @@ impl OpenCADStudio {
let name = parts.get(1).copied().unwrap_or("").to_string(); let name = parts.get(1).copied().unwrap_or("").to_string();
if name.is_empty() || name.eq_ignore_ascii_case("Standard") { if name.is_empty() || name.eq_ignore_ascii_case("Standard") {
self.command_line self.command_line
.push_error("Cannot delete the Standard style."); .push_error(crate::t!("Cannot delete the Standard style.").as_ref());
} else { } else {
let doc = &self.tabs[i].scene.document; let doc = &self.tabs[i].scene.document;
let handle = doc.objects.iter().find_map(|(&h, o)| { let handle = doc.objects.iter().find_map(|(&h, o)| {
@ -776,16 +776,16 @@ impl OpenCADStudio {
self.tabs[i].scene.document.objects.remove(&h); self.tabs[i].scene.document.objects.remove(&h);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("MLSTYLE: '{}' deleted.", name)); .push_output(crate::tf!("MLSTYLE: '{}' deleted.", name).as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("MLSTYLE: '{}' not found.", name)); .push_error(crate::tf!("MLSTYLE: '{}' not found.", name).as_ref());
} }
} }
} }
_ => { _ => {
self.command_line self.command_line
.push_error("Usage: MLSTYLE [LIST|NEW <name>|SET <name>|DEL <name>]"); .push_error(crate::t!("Usage: MLSTYLE [LIST|NEW <name>|SET <name>|DEL <name>]").as_ref());
} }
} }
} }
@ -813,19 +813,19 @@ impl OpenCADStudio {
.map(|s| format!("{}(txt:{:.2} asz:{:.2})", s.name, s.dimtxt, s.dimasz)) .map(|s| format!("{}(txt:{:.2} asz:{:.2})", s.name, s.dimtxt, s.dimasz))
.collect(); .collect();
if styles.is_empty() { if styles.is_empty() {
self.command_line.push_output("No dim styles defined."); self.command_line.push_output(crate::t!("No dim styles defined.").as_ref());
} else { } else {
self.command_line self.command_line
.push_output(&format!("DimStyles: {}", styles.join(", "))); .push_output(crate::tf!("DimStyles: {}", styles.join(", ")).as_ref());
} }
} }
"NEW" | "N" => { "NEW" | "N" => {
let name = parts.get(1).map(|s| s.trim()).unwrap_or("").to_string(); let name = parts.get(1).map(|s| s.trim()).unwrap_or("").to_string();
if name.is_empty() { if name.is_empty() {
self.command_line.push_error("Usage: DIMSTYLE NEW <name>"); self.command_line.push_error(crate::t!("Usage: DIMSTYLE NEW <name>").as_ref());
} else if self.tabs[i].scene.document.dim_styles.contains(&name) { } else if self.tabs[i].scene.document.dim_styles.contains(&name) {
self.command_line self.command_line
.push_error(&format!("DIMSTYLE: '{}' already exists.", name)); .push_error(crate::tf!("DIMSTYLE: '{}' already exists.", name).as_ref());
} else { } else {
let undo = self.begin_dim_style_undo( let undo = self.begin_dim_style_undo(
i, i,
@ -837,7 +837,7 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.commit_dim_style_undo(i, undo); self.commit_dim_style_undo(i, undo);
self.command_line self.command_line
.push_output(&format!("DIMSTYLE: '{}' created.", name)); .push_output(crate::tf!("DIMSTYLE: '{}' created.", name).as_ref());
} }
} }
"SET" | "S" => { "SET" | "S" => {
@ -896,9 +896,9 @@ impl OpenCADStudio {
ds.dimfxl = val; ds.dimfxl = val;
} }
_ => { _ => {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"DIMSTYLE: unknown property '{}'. Try: dimtxt dimasz dimdli dimexo dimexe dimgap dimscale dimlfac dimdle dimcen dimtsz", prop "DIMSTYLE: unknown property '{}'. Try: dimtxt dimasz dimdli dimexo dimexe dimgap dimscale dimlfac dimdle dimcen dimtsz", prop
)); ).as_ref());
return Some(Task::none()); return Some(Task::none());
} }
} }
@ -906,16 +906,16 @@ impl OpenCADStudio {
self.tabs[i].scene self.tabs[i].scene
.invalidate_dim_style_dependencies(&style_name); .invalidate_dim_style_dependencies(&style_name);
self.commit_dim_style_undo(i, undo); self.commit_dim_style_undo(i, undo);
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"DIMSTYLE: '{style_name}'.{prop} = {val:.3}" "DIMSTYLE: '{style_name}'.{prop} = {val:.3}"
)); ).as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("DIMSTYLE: '{}' not found.", style_name)); .push_error(crate::tf!("DIMSTYLE: '{}' not found.", style_name).as_ref());
} }
} else { } else {
self.command_line self.command_line
.push_error("Usage: DIMSTYLE SET <name> <property> <value>"); .push_error(crate::t!("Usage: DIMSTYLE SET <name> <property> <value>").as_ref());
} }
} }
_ => { _ => {
@ -956,28 +956,28 @@ impl OpenCADStudio {
let current = &self.tabs[i].active_mleader_style; let current = &self.tabs[i].active_mleader_style;
if styles.is_empty() { if styles.is_empty() {
self.command_line self.command_line
.push_output(&format!("MLeader styles: (none) active: {current}")); .push_output(crate::tf!("MLeader styles: (none) active: {current}").as_ref());
} else { } else {
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"MLeader styles: {} active: {current}", "MLeader styles: {} active: {current}",
styles.join(", ") styles.join(", ")
)); ).as_ref());
} }
} }
"NEW" | "N" => { "NEW" | "N" => {
let name = parts.get(1).map(|s| s.trim()).unwrap_or("").to_string(); let name = parts.get(1).map(|s| s.trim()).unwrap_or("").to_string();
if name.is_empty() { if name.is_empty() {
self.command_line self.command_line
.push_error("Usage: MLEADERSTYLE NEW <name>"); .push_error(crate::t!("Usage: MLEADERSTYLE NEW <name>").as_ref());
} else { } else {
let already_exists = self.tabs[i].scene.document.objects.values().any( let already_exists = self.tabs[i].scene.document.objects.values().any(
|o| matches!(o, ObjectType::MultiLeaderStyle(s) if s.name == name), |o| matches!(o, ObjectType::MultiLeaderStyle(s) if s.name == name),
); );
if already_exists { if already_exists {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"MLEADERSTYLE: '{}' already exists.", "MLEADERSTYLE: '{}' already exists.",
name name
)); ).as_ref());
} else { } else {
let handle = self.tabs[i].scene.document.allocate_handle(); let handle = self.tabs[i].scene.document.allocate_handle();
let mut style = MultiLeaderStyle::new(&name); let mut style = MultiLeaderStyle::new(&name);
@ -990,7 +990,7 @@ impl OpenCADStudio {
self.push_undo_snapshot(i, "MLEADERSTYLE NEW"); self.push_undo_snapshot(i, "MLEADERSTYLE NEW");
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("MLEADERSTYLE: '{}' created.", name)); .push_output(crate::tf!("MLEADERSTYLE: '{}' created.", name).as_ref());
} }
} }
} }
@ -1032,52 +1032,52 @@ impl OpenCADStudio {
s.landing_gap = val; s.landing_gap = val;
} }
_ => { _ => {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"MLEADERSTYLE: unknown property '{}'. Try: text_height arrowhead_size landing_distance landing_gap", prop "MLEADERSTYLE: unknown property '{}'. Try: text_height arrowhead_size landing_distance landing_gap", prop
)); ).as_ref());
return Some(Task::none()); return Some(Task::none());
} }
} }
self.push_undo_snapshot(i, "MLEADERSTYLE SET"); self.push_undo_snapshot(i, "MLEADERSTYLE SET");
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"MLEADERSTYLE: '{style_name}'.{prop} = {val:.3}" "MLEADERSTYLE: '{style_name}'.{prop} = {val:.3}"
)); ).as_ref());
} else { } else {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"MLEADERSTYLE: '{}' not found.", "MLEADERSTYLE: '{}' not found.",
style_name style_name
)); ).as_ref());
} }
} else { } else {
self.command_line self.command_line
.push_error("Usage: MLEADERSTYLE SET <name> <property> <value>"); .push_error(crate::t!("Usage: MLEADERSTYLE SET <name> <property> <value>").as_ref());
} }
} }
"CURRENT" | "C" | "ACTIVE" => { "CURRENT" | "C" | "ACTIVE" => {
let name = parts.get(1).map(|s| s.trim()).unwrap_or("").to_string(); let name = parts.get(1).map(|s| s.trim()).unwrap_or("").to_string();
if name.is_empty() { if name.is_empty() {
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Current MLeader style: {}", "Current MLeader style: {}",
self.tabs[i].active_mleader_style self.tabs[i].active_mleader_style
)); ).as_ref());
} else { } else {
let exists = name == "Standard" || self.tabs[i].scene.document.objects.values() let exists = name == "Standard" || self.tabs[i].scene.document.objects.values()
.any(|o| matches!(o, ObjectType::MultiLeaderStyle(s) if s.name == name)); .any(|o| matches!(o, ObjectType::MultiLeaderStyle(s) if s.name == name));
if exists { if exists {
self.tabs[i].active_mleader_style = name.clone(); self.tabs[i].active_mleader_style = name.clone();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"MLEADERSTYLE: current style set to '{name}'." "MLEADERSTYLE: current style set to '{name}'."
)); ).as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("MLEADERSTYLE: '{}' not found.", name)); .push_error(crate::tf!("MLEADERSTYLE: '{}' not found.", name).as_ref());
} }
} }
} }
_ => { _ => {
self.command_line.push_info( self.command_line.push_info(
"Usage: MLEADERSTYLE LIST | NEW <name> | SET <name> <prop> <val> | CURRENT [<name>]" crate::t!("Usage: MLEADERSTYLE LIST | NEW <name> | SET <name> <prop> <val> | CURRENT [<name>]").as_ref()
); );
} }
} }
@ -1117,10 +1117,10 @@ impl OpenCADStudio {
}) })
.collect(); .collect();
if styles.is_empty() { if styles.is_empty() {
self.command_line.push_output("No text styles defined."); self.command_line.push_output(crate::t!("No text styles defined.").as_ref());
} else { } else {
self.command_line self.command_line
.push_output(&format!("Text styles: {}", styles.join(" | "))); .push_output(crate::tf!("Text styles: {}", styles.join(" | ")).as_ref());
} }
} }
"SET" | "S" => { "SET" | "S" => {
@ -1128,20 +1128,20 @@ impl OpenCADStudio {
let name = parts.get(1).map(|s| s.trim()).unwrap_or(""); let name = parts.get(1).map(|s| s.trim()).unwrap_or("");
if self.tabs[i].scene.document.text_styles.get(name).is_some() { if self.tabs[i].scene.document.text_styles.get(name).is_some() {
self.command_line self.command_line
.push_output(&format!("{prefix}: active style set to '{name}'.")); .push_output(crate::tf!("{prefix}: active style set to '{name}'.").as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("{prefix}: style '{name}' not found.")); .push_error(crate::tf!("{prefix}: style '{name}' not found.").as_ref());
} }
} }
"NEW" | "N" => { "NEW" | "N" => {
let name = parts.get(1).map(|s| s.trim()).unwrap_or("").to_string(); let name = parts.get(1).map(|s| s.trim()).unwrap_or("").to_string();
if name.is_empty() { if name.is_empty() {
self.command_line self.command_line
.push_error(&format!("Usage: {prefix} NEW <name>")); .push_error(crate::tf!("Usage: {prefix} NEW <name>").as_ref());
} else if self.tabs[i].scene.document.text_styles.contains(&name) { } else if self.tabs[i].scene.document.text_styles.contains(&name) {
self.command_line self.command_line
.push_error(&format!("{prefix}: style '{name}' already exists.")); .push_error(crate::tf!("{prefix}: style '{name}' already exists.").as_ref());
} else { } else {
let undo = self.begin_text_style_undo( let undo = self.begin_text_style_undo(
i, i,
@ -1153,7 +1153,7 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.commit_text_style_undo(i, undo); self.commit_text_style_undo(i, undo);
self.command_line self.command_line
.push_output(&format!("{prefix}: style '{name}' created.")); .push_output(crate::tf!("{prefix}: style '{name}' created.").as_ref());
} }
} }
"FONT" | "F" => { "FONT" | "F" => {
@ -1162,7 +1162,7 @@ impl OpenCADStudio {
let font = parts.get(2).map(|s| s.trim()).unwrap_or("").to_string(); let font = parts.get(2).map(|s| s.trim()).unwrap_or("").to_string();
if style_name.is_empty() || font.is_empty() { if style_name.is_empty() || font.is_empty() {
self.command_line self.command_line
.push_error(&format!("Usage: {prefix} FONT <style> <font_file>")); .push_error(crate::tf!("Usage: {prefix} FONT <style> <font_file>").as_ref());
} else { } else {
let undo = self.begin_text_style_undo( let undo = self.begin_text_style_undo(
i, i,
@ -1178,12 +1178,12 @@ impl OpenCADStudio {
.scene .scene
.invalidate_text_style_dependencies(&style_name); .invalidate_text_style_dependencies(&style_name);
self.commit_text_style_undo(i, undo); self.commit_text_style_undo(i, undo);
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"{prefix}: '{style_name}' font set to '{font}'." "{prefix}: '{style_name}' font set to '{font}'."
)); ).as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("{prefix}: style '{style_name}' not found.")); .push_error(crate::tf!("{prefix}: style '{style_name}' not found.").as_ref());
} }
} }
} }
@ -1206,17 +1206,17 @@ impl OpenCADStudio {
.scene .scene
.invalidate_text_style_dependencies(&style_name); .invalidate_text_style_dependencies(&style_name);
self.commit_text_style_undo(i, undo); self.commit_text_style_undo(i, undo);
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"{prefix}: '{style_name}' width factor set to {factor:.3}." "{prefix}: '{style_name}' width factor set to {factor:.3}."
)); ).as_ref());
} else { } else {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"{prefix}: style '{style_name}' not found." "{prefix}: style '{style_name}' not found."
)); ).as_ref());
} }
} else { } else {
self.command_line self.command_line
.push_error(&format!("Usage: {prefix} WIDTH <style> <factor>")); .push_error(crate::tf!("Usage: {prefix} WIDTH <style> <factor>").as_ref());
} }
} }
"OBLIQUE" => { "OBLIQUE" => {
@ -1238,24 +1238,24 @@ impl OpenCADStudio {
.scene .scene
.invalidate_text_style_dependencies(&style_name); .invalidate_text_style_dependencies(&style_name);
self.commit_text_style_undo(i, undo); self.commit_text_style_undo(i, undo);
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"{prefix}: '{style_name}' oblique angle set to {deg:.1}°." "{prefix}: '{style_name}' oblique angle set to {deg:.1}°."
)); ).as_ref());
} else { } else {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"{prefix}: style '{style_name}' not found." "{prefix}: style '{style_name}' not found."
)); ).as_ref());
} }
} else { } else {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"Usage: {prefix} OBLIQUE <style> <angle_degrees>" "Usage: {prefix} OBLIQUE <style> <angle_degrees>"
)); ).as_ref());
} }
} }
_ => { _ => {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"Usage: {prefix} LIST | NEW <name> | FONT <style> <file> | WIDTH <style> <factor> | OBLIQUE <style> <angle>" "Usage: {prefix} LIST | NEW <name> | FONT <style> <file> | WIDTH <style> <factor> | OBLIQUE <style> <angle>"
)); ).as_ref());
} }
} }
} }

View file

@ -37,7 +37,7 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.commit_layer_undo(i, undo); self.commit_layer_undo(i, undo);
self.refresh_layer_panel(); self.refresh_layer_panel();
self.command_line.push_info("Layer(s) turned off."); self.command_line.push_info(crate::t!("Layer(s) turned off.").as_ref());
} }
} }
@ -74,7 +74,7 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.commit_layer_undo(i, undo); self.commit_layer_undo(i, undo);
self.refresh_layer_panel(); self.refresh_layer_panel();
self.command_line.push_info("Layer(s) frozen."); self.command_line.push_info(crate::t!("Layer(s) frozen.").as_ref());
} }
} }
@ -88,7 +88,7 @@ impl OpenCADStudio {
cmd if cmd.starts_with("LAYDEL ") => { cmd if cmd.starts_with("LAYDEL ") => {
let name = cmd.trim_start_matches("LAYDEL").trim(); let name = cmd.trim_start_matches("LAYDEL").trim();
if name.is_empty() { if name.is_empty() {
self.command_line.push_info("Usage: LAYDEL <layer name>"); self.command_line.push_info(crate::t!("Usage: LAYDEL <layer name>").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
let resolved = self.tabs[i] let resolved = self.tabs[i]
@ -100,12 +100,12 @@ impl OpenCADStudio {
.map(|s| s.to_string()); .map(|s| s.to_string());
let Some(layer) = resolved else { let Some(layer) = resolved else {
self.command_line self.command_line
.push_error(&format!("LAYDEL: no layer named \"{name}\".")); .push_error(crate::tf!("LAYDEL: no layer named \"{name}\".").as_ref());
return Some(Task::none()); return Some(Task::none());
}; };
if layer == "0" { if layer == "0" {
self.command_line self.command_line
.push_error("LAYDEL: layer \"0\" cannot be deleted."); .push_error(crate::t!("LAYDEL: layer \"0\" cannot be deleted.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
if layer.eq_ignore_ascii_case(&self.tabs[i].active_layer) { if layer.eq_ignore_ascii_case(&self.tabs[i].active_layer) {
@ -130,9 +130,9 @@ impl OpenCADStudio {
self.tabs[i].scene.bump_geometry(); self.tabs[i].scene.bump_geometry();
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_layer_panel(); self.refresh_layer_panel();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"LAYDEL: deleted layer \"{layer}\" and {n} object(s)." "LAYDEL: deleted layer \"{layer}\" and {n} object(s)."
)); ).as_ref());
} }
// LAYMRG <source> <target> — move every object from <source> onto // LAYMRG <source> <target> — move every object from <source> onto
@ -152,7 +152,7 @@ impl OpenCADStudio {
let parts: Vec<&str> = rest.split_whitespace().collect(); let parts: Vec<&str> = rest.split_whitespace().collect();
if parts.len() != 2 { if parts.len() != 2 {
self.command_line self.command_line
.push_info("Usage: LAYMRG <source layer> <target layer>"); .push_info(crate::t!("Usage: LAYMRG <source layer> <target layer>").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
let keys: Vec<String> = self.tabs[i] let keys: Vec<String> = self.tabs[i]
@ -172,17 +172,17 @@ impl OpenCADStudio {
.cloned(); .cloned();
let (Some(src), Some(dst)) = (src, dst) else { let (Some(src), Some(dst)) = (src, dst) else {
self.command_line self.command_line
.push_error("LAYMRG: source and target layers must both exist."); .push_error(crate::t!("LAYMRG: source and target layers must both exist.").as_ref());
return Some(Task::none()); return Some(Task::none());
}; };
if src == dst { if src == dst {
self.command_line self.command_line
.push_error("LAYMRG: source and target are the same layer."); .push_error(crate::t!("LAYMRG: source and target are the same layer.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
if src == "0" { if src == "0" {
self.command_line self.command_line
.push_error("LAYMRG: layer \"0\" cannot be merged away."); .push_error(crate::t!("LAYMRG: layer \"0\" cannot be merged away.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
if src.eq_ignore_ascii_case(&self.tabs[i].active_layer) { if src.eq_ignore_ascii_case(&self.tabs[i].active_layer) {
@ -206,9 +206,9 @@ impl OpenCADStudio {
.invalidate_layer_dependencies(std::slice::from_ref(&dst)); .invalidate_layer_dependencies(std::slice::from_ref(&dst));
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_layer_panel(); self.refresh_layer_panel();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"LAYMRG: merged \"{src}\" into \"{dst}\" ({moved} object(s))." "LAYMRG: merged \"{src}\" into \"{dst}\" ({moved} object(s))."
)); ).as_ref());
} }
// LAYERSTATE — save / restore named snapshots of all layer states // LAYERSTATE — save / restore named snapshots of all layer states
@ -241,12 +241,12 @@ impl OpenCADStudio {
states.iter().map(|state| state.name.as_str()).collect(); states.iter().map(|state| state.name.as_str()).collect();
names.sort_unstable(); names.sort_unstable();
self.command_line self.command_line
.push_output(&format!("Saved layer states: {}", names.join(", "))); .push_output(crate::tf!("Saved layer states: {}", names.join(", ")).as_ref());
} }
} }
"SAVE" | "S" => { "SAVE" | "S" => {
if arg.is_empty() { if arg.is_empty() {
self.command_line.push_info("Usage: LAYERSTATE SAVE <name>"); self.command_line.push_info(crate::t!("Usage: LAYERSTATE SAVE <name>").as_ref());
} else { } else {
let description = self.tabs[i] let description = self.tabs[i]
.scene .scene
@ -261,17 +261,17 @@ impl OpenCADStudio {
.capture_layer_state(arg, description); .capture_layer_state(arg, description);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("LAYERSTATE: saved \"{arg}\".")); .push_output(crate::tf!("LAYERSTATE: saved \"{arg}\".").as_ref());
} }
} }
"RESTORE" | "R" => { "RESTORE" | "R" => {
if arg.is_empty() { if arg.is_empty() {
self.command_line self.command_line
.push_info("Usage: LAYERSTATE RESTORE <name>"); .push_info(crate::t!("Usage: LAYERSTATE RESTORE <name>").as_ref());
} else if self.tabs[i].scene.document.layer_state(arg).is_none() { } else if self.tabs[i].scene.document.layer_state(arg).is_none() {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"LAYERSTATE: no saved state named \"{arg}\"." "LAYERSTATE: no saved state named \"{arg}\"."
)); ).as_ref());
} else { } else {
let names: Vec<String> = self.tabs[i] let names: Vec<String> = self.tabs[i]
.scene .scene
@ -295,30 +295,30 @@ impl OpenCADStudio {
self.tabs[i].scene.invalidate_layer_dependencies(&names); self.tabs[i].scene.invalidate_layer_dependencies(&names);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_layer_panel(); self.refresh_layer_panel();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"LAYERSTATE: restored \"{arg}\" ({n} layer(s))." "LAYERSTATE: restored \"{arg}\" ({n} layer(s))."
)); ).as_ref());
} }
} }
"DELETE" | "D" => { "DELETE" | "D" => {
if arg.is_empty() { if arg.is_empty() {
self.command_line self.command_line
.push_info("Usage: LAYERSTATE DELETE <name>"); .push_info(crate::t!("Usage: LAYERSTATE DELETE <name>").as_ref());
} else if self.tabs[i].scene.document.layer_state(arg).is_none() { } else if self.tabs[i].scene.document.layer_state(arg).is_none() {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"LAYERSTATE: no saved state named \"{arg}\"." "LAYERSTATE: no saved state named \"{arg}\"."
)); ).as_ref());
} else { } else {
self.push_undo_snapshot(i, "LAYERSTATE DELETE"); self.push_undo_snapshot(i, "LAYERSTATE DELETE");
self.tabs[i].scene.document.delete_layer_state(arg); self.tabs[i].scene.document.delete_layer_state(arg);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("LAYERSTATE: deleted \"{arg}\".")); .push_output(crate::tf!("LAYERSTATE: deleted \"{arg}\".").as_ref());
} }
} }
_ => { _ => {
self.command_line self.command_line
.push_info("Usage: LAYERSTATE SAVE|RESTORE|DELETE <name> | ? (list)"); .push_info(crate::t!("Usage: LAYERSTATE SAVE|RESTORE|DELETE <name> | ? (list)").as_ref());
} }
} }
} }
@ -353,7 +353,7 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.commit_layer_undo(i, undo); self.commit_layer_undo(i, undo);
self.refresh_layer_panel(); self.refresh_layer_panel();
self.command_line.push_info("Layer(s) locked."); self.command_line.push_info(crate::t!("Layer(s) locked.").as_ref());
} }
} }
@ -385,7 +385,7 @@ impl OpenCADStudio {
self.tabs[i].layers.current_layer = layer.clone(); self.tabs[i].layers.current_layer = layer.clone();
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_info(&format!("Current layer set to \"{layer}\".")); .push_info(crate::tf!("Current layer set to \"{layer}\".").as_ref());
self.refresh_layer_panel(); self.refresh_layer_panel();
} }
} }
@ -408,7 +408,7 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.commit_layer_undo(i, undo); self.commit_layer_undo(i, undo);
self.refresh_layer_panel(); self.refresh_layer_panel();
self.command_line.push_info("All layers turned on."); self.command_line.push_info(crate::t!("All layers turned on.").as_ref());
} }
"LAYTHW" => { "LAYTHW" => {
@ -429,7 +429,7 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.commit_layer_undo(i, undo); self.commit_layer_undo(i, undo);
self.refresh_layer_panel(); self.refresh_layer_panel();
self.command_line.push_info("All layers thawed."); self.command_line.push_info(crate::t!("All layers thawed.").as_ref());
} }
"LAYULK" => { "LAYULK" => {
@ -462,7 +462,7 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.commit_layer_undo(i, undo); self.commit_layer_undo(i, undo);
self.refresh_layer_panel(); self.refresh_layer_panel();
self.command_line.push_info("Layer(s) unlocked."); self.command_line.push_info(crate::t!("Layer(s) unlocked.").as_ref());
} }
} }
@ -476,7 +476,7 @@ impl OpenCADStudio {
.collect(); .collect();
if sel_layers.is_empty() { if sel_layers.is_empty() {
self.command_line self.command_line
.push_error("LAYISO: select entities on the layers to isolate first."); .push_error(crate::t!("LAYISO: select entities on the layers to isolate first.").as_ref());
} else { } else {
let names: Vec<String> = self.tabs[i] let names: Vec<String> = self.tabs[i]
.scene .scene
@ -498,7 +498,7 @@ impl OpenCADStudio {
self.commit_layer_undo(i, undo); self.commit_layer_undo(i, undo);
self.refresh_layer_panel(); self.refresh_layer_panel();
self.command_line self.command_line
.push_info(&format!("LAYISO: isolated {} layer(s).", sel_layers.len())); .push_info(crate::tf!("LAYISO: isolated {} layer(s).", sel_layers.len()).as_ref());
} }
} }
@ -506,7 +506,7 @@ impl OpenCADStudio {
"ISOLATEOBJECTS" => { "ISOLATEOBJECTS" => {
if self.tabs[i].scene.selected.is_empty() { if self.tabs[i].scene.selected.is_empty() {
self.command_line self.command_line
.push_error("ISOLATEOBJECTS: select the objects to isolate first."); .push_error(crate::t!("ISOLATEOBJECTS: select the objects to isolate first.").as_ref());
} else { } else {
let n = self.tabs[i].scene.selected.len(); let n = self.tabs[i].scene.selected.len();
let before = self.tabs[i].scene.object_isolation.clone(); let before = self.tabs[i].scene.object_isolation.clone();
@ -519,9 +519,9 @@ impl OpenCADStudio {
before, before,
selected_before, selected_before,
); );
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"Isolated {n} object(s). UNISOLATEOBJECTS to restore." "Isolated {n} object(s). UNISOLATEOBJECTS to restore."
)); ).as_ref());
} }
} }
@ -529,7 +529,7 @@ impl OpenCADStudio {
"HIDEOBJECTS" => { "HIDEOBJECTS" => {
if self.tabs[i].scene.selected.is_empty() { if self.tabs[i].scene.selected.is_empty() {
self.command_line self.command_line
.push_error("HIDEOBJECTS: select the objects to hide first."); .push_error(crate::t!("HIDEOBJECTS: select the objects to hide first.").as_ref());
} else { } else {
let n = self.tabs[i].scene.selected.len(); let n = self.tabs[i].scene.selected.len();
let before = self.tabs[i].scene.object_isolation.clone(); let before = self.tabs[i].scene.object_isolation.clone();
@ -544,7 +544,7 @@ impl OpenCADStudio {
); );
self.refresh_properties(); self.refresh_properties();
self.command_line self.command_line
.push_info(&format!("Hid {n} object(s). UNISOLATEOBJECTS to restore.")); .push_info(crate::tf!("Hid {n} object(s). UNISOLATEOBJECTS to restore.").as_ref());
} }
} }
@ -562,9 +562,9 @@ impl OpenCADStudio {
selected_before, selected_before,
); );
self.command_line self.command_line
.push_info("Isolation ended — all objects shown."); .push_info(crate::t!("Isolation ended — all objects shown.").as_ref());
} else { } else {
self.command_line.push_info("No hidden objects."); self.command_line.push_info(crate::t!("No hidden objects.").as_ref());
} }
} }
@ -588,7 +588,7 @@ impl OpenCADStudio {
self.commit_layer_undo(i, undo); self.commit_layer_undo(i, undo);
self.refresh_layer_panel(); self.refresh_layer_panel();
self.command_line self.command_line
.push_info("LAYUNISO: all layers restored."); .push_info(crate::t!("LAYUNISO: all layers restored.").as_ref());
} }
"LAYMATCH" | "LAYMCH" => { "LAYMATCH" | "LAYMCH" => {
@ -653,10 +653,10 @@ impl OpenCADStudio {
self.commit_group_undo(i, undo); self.commit_group_undo(i, undo);
if count > 0 { if count > 0 {
self.command_line self.command_line
.push_info(&format!("{} group(s) dissolved.", count)); .push_info(crate::tf!("{} group(s) dissolved.", count).as_ref());
} else { } else {
self.command_line self.command_line
.push_info("No groups found for selected objects."); .push_info(crate::t!("No groups found for selected objects.").as_ref());
} }
} }
} }

View file

@ -116,7 +116,7 @@ impl OpenCADStudio {
// rather than keeping a second, blunter copy (#388, #389). // rather than keeping a second, blunter copy (#388, #389).
if self.tabs[i].is_start && !start_allowed(cmd) { if self.tabs[i].is_start && !start_allowed(cmd) {
self.command_line self.command_line
.push_info("No drawing open. Use NEW or OPEN to start a drawing."); .push_info(crate::t!("No drawing open. Use NEW or OPEN to start a drawing.").as_ref());
return Task::none(); return Task::none();
} }
@ -164,7 +164,7 @@ impl OpenCADStudio {
} }
} }
self.command_line self.command_line
.push_error(&format!("Unknown command: {cmd}")); .push_error(crate::tf!("Unknown command: {cmd}").as_ref());
self.finish_dispatch(cmd) self.finish_dispatch(cmd)
} }

View file

@ -38,7 +38,7 @@ impl OpenCADStudio {
"" => { "" => {
let c = self.tabs[i].scene.document.header.current_entity_color; let c = self.tabs[i].scene.document.header.current_entity_color;
self.command_line self.command_line
.push_output(&format!("Current object colour: {}", describe(&c))); .push_output(crate::tf!("Current object colour: {}", describe(&c)).as_ref());
return Some(Task::none()); return Some(Task::none());
} }
"BYLAYER" => Some(Color::ByLayer), "BYLAYER" => Some(Color::ByLayer),
@ -58,7 +58,7 @@ impl OpenCADStudio {
self.ribbon.active_color = c; self.ribbon.active_color = c;
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("Object colour set to {}.", describe(&c))); .push_output(crate::tf!("Object colour set to {}.", describe(&c)).as_ref());
} }
None => { None => {
self.command_line.push_error( self.command_line.push_error(
@ -96,10 +96,10 @@ impl OpenCADStudio {
.map(|lt| format!("{} ({})", lt.name, lt.description)) .map(|lt| format!("{} ({})", lt.name, lt.description))
.collect(); .collect();
if ltypes.is_empty() { if ltypes.is_empty() {
self.command_line.push_output("No linetypes defined."); self.command_line.push_output(crate::t!("No linetypes defined.").as_ref());
} else { } else {
self.command_line self.command_line
.push_output(&format!("Linetypes: {}", ltypes.join(", "))); .push_output(crate::tf!("Linetypes: {}", ltypes.join(", ")).as_ref());
} }
} }
// Set the current linetype applied to newly drawn entities. // Set the current linetype applied to newly drawn entities.
@ -107,7 +107,7 @@ impl OpenCADStudio {
let name = parts.get(1).copied().unwrap_or(""); let name = parts.get(1).copied().unwrap_or("");
if name.is_empty() { if name.is_empty() {
self.command_line self.command_line
.push_info("Usage: LINETYPE SET <name | ByLayer | ByBlock>"); .push_info(crate::t!("Usage: LINETYPE SET <name | ByLayer | ByBlock>").as_ref());
} else { } else {
let canon = if name.eq_ignore_ascii_case("BYLAYER") { let canon = if name.eq_ignore_ascii_case("BYLAYER") {
Some(("ByLayer".to_string(), acadrust::types::Handle::NULL)) Some(("ByLayer".to_string(), acadrust::types::Handle::NULL))
@ -129,19 +129,19 @@ impl OpenCADStudio {
h.current_linetype_handle = handle; h.current_linetype_handle = handle;
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("Current linetype set to {nm}.")); .push_output(crate::tf!("Current linetype set to {nm}.").as_ref());
} }
None => { None => {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"LINETYPE: \"{name}\" is not loaded. Use LINETYPE LIST to see available linetypes." "LINETYPE: \"{name}\" is not loaded. Use LINETYPE LIST to see available linetypes."
)); ).as_ref());
} }
} }
} }
} }
_ => { _ => {
self.command_line self.command_line
.push_info("Usage: LINETYPE LIST | SET <name>"); .push_info(crate::t!("Usage: LINETYPE LIST | SET <name>").as_ref());
} }
} }
} }
@ -177,9 +177,9 @@ impl OpenCADStudio {
let do_lts = all || matches!(sub.as_str(), "LINETYPES" | "LT"); let do_lts = all || matches!(sub.as_str(), "LINETYPES" | "LT");
let do_blocks = all || matches!(sub.as_str(), "BLOCKS" | "BLOCK" | "B"); let do_blocks = all || matches!(sub.as_str(), "BLOCKS" | "BLOCK" | "B");
if !(do_layers || do_styles || do_lts || do_blocks) { if !(do_layers || do_styles || do_lts || do_blocks) {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"PURGE: unknown option \"{sub}\" — use ALL, Blocks, LAyers, LineTypes or STyles." "PURGE: unknown option \"{sub}\" — use ALL, Blocks, LAyers, LineTypes or STyles."
)); ).as_ref());
return Some(Task::none()); return Some(Task::none());
} }
@ -560,13 +560,13 @@ impl OpenCADStudio {
if n_sortents > 0 { if n_sortents > 0 {
parts.push(format!("{n_sortents} stale draw-order table(s)")); parts.push(format!("{n_sortents} stale draw-order table(s)"));
} }
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"PURGE: {} item(s) removed — {}.", "PURGE: {} item(s) removed — {}.",
purged + n_sortents, purged + n_sortents,
parts.join(", ") parts.join(", ")
)); ).as_ref());
} else { } else {
self.command_line.push_output("PURGE: nothing to purge."); self.command_line.push_output(crate::t!("PURGE: nothing to purge.").as_ref());
} }
} }
@ -610,7 +610,7 @@ impl OpenCADStudio {
.collect(); .collect();
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_error("CHPROP: no entities selected."); .push_error(crate::t!("CHPROP: no entities selected.").as_ref());
} else { } else {
// Validate value early to give clear errors // Validate value early to give clear errors
let color_val: Option<acadrust::types::Color> = if prop == "COLOR" { let color_val: Option<acadrust::types::Color> = if prop == "COLOR" {
@ -640,10 +640,10 @@ impl OpenCADStudio {
|| (prop == "LTSCALE" && ltscale_val.is_none()) || (prop == "LTSCALE" && ltscale_val.is_none())
|| (prop == "TRANSPARENCY" && transparency_val.is_none()) || (prop == "TRANSPARENCY" && transparency_val.is_none())
{ {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"CHPROP: invalid value '{}' for {}.", "CHPROP: invalid value '{}' for {}.",
value, prop value, prop
)); ).as_ref());
} else { } else {
let mut changed = 0usize; let mut changed = 0usize;
for handle in &handles { for handle in &handles {
@ -673,9 +673,9 @@ impl OpenCADStudio {
changed += 1; changed += 1;
} }
_ => { _ => {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"CHPROP: unknown property '{}'. Use: LAYER COLOR LINETYPE LTSCALE TRANSPARENCY", prop "CHPROP: unknown property '{}'. Use: LAYER COLOR LINETYPE LTSCALE TRANSPARENCY", prop
)); ).as_ref());
break; break;
} }
} }
@ -689,10 +689,10 @@ impl OpenCADStudio {
// re-tessellate the changed entities so they // re-tessellate the changed entities so they
// repaint immediately (issue #231 class). // repaint immediately (issue #231 class).
self.invalidate_property_targets(i, &handles); self.invalidate_property_targets(i, &handles);
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"CHPROP: {} entity/entities updated.", "CHPROP: {} entity/entities updated.",
changed changed
)); ).as_ref());
} }
} }
} }
@ -711,7 +711,7 @@ impl OpenCADStudio {
.collect(); .collect();
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_error("SETBYLAYER: select entities first."); .push_error(crate::t!("SETBYLAYER: select entities first.").as_ref());
} else { } else {
self.push_undo_snapshot(i, "SETBYLAYER"); self.push_undo_snapshot(i, "SETBYLAYER");
let mut changed = 0usize; let mut changed = 0usize;
@ -730,9 +730,9 @@ impl OpenCADStudio {
.map(|handle| (handle, crate::scene::ChangeKind::Modified)) .map(|handle| (handle, crate::scene::ChangeKind::Modified))
.collect(); .collect();
self.tabs[i].scene.bump_entities(&changes); self.tabs[i].scene.bump_entities(&changes);
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"SETBYLAYER: reset {changed} entity/entities to ByLayer." "SETBYLAYER: reset {changed} entity/entities to ByLayer."
)); ).as_ref());
} }
} }
@ -778,7 +778,7 @@ impl OpenCADStudio {
} }
if dups.is_empty() { if dups.is_empty() {
self.command_line self.command_line
.push_output("OVERKILL: no duplicate objects found."); .push_output(crate::t!("OVERKILL: no duplicate objects found.").as_ref());
} else { } else {
let n = dups.len(); let n = dups.len();
self.push_undo_snapshot(i, "OVERKILL"); self.push_undo_snapshot(i, "OVERKILL");
@ -786,7 +786,7 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
self.command_line self.command_line
.push_output(&format!("OVERKILL: deleted {n} duplicate object(s).")); .push_output(crate::tf!("OVERKILL: deleted {n} duplicate object(s).").as_ref());
} }
} }
@ -813,17 +813,17 @@ impl OpenCADStudio {
} else { } else {
u8::from(self.pick_drag_rect) u8::from(self.pick_drag_rect)
}; };
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"{} = {v}", "{} = {v}",
if is_add { "PICKADD" } else { "PICKDRAG" } if is_add { "PICKADD" } else { "PICKDRAG" }
)); ).as_ref());
} else { } else {
match arg { match arg {
"0" | "1" => { "0" | "1" => {
let on = arg == "1"; let on = arg == "1";
if is_add { if is_add {
self.pick_add = on; self.pick_add = on;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"PICKADD = {} ({})", "PICKADD = {} ({})",
arg, arg,
if on { if on {
@ -831,10 +831,10 @@ impl OpenCADStudio {
} else { } else {
"click replaces selection, Shift toggles" "click replaces selection, Shift toggles"
} }
)); ).as_ref());
} else { } else {
self.pick_drag_rect = on; self.pick_drag_rect = on;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"PICKDRAG = {} ({})", "PICKDRAG = {} ({})",
arg, arg,
if on { if on {
@ -842,14 +842,14 @@ impl OpenCADStudio {
} else { } else {
"press-drag lassoes" "press-drag lassoes"
} }
)); ).as_ref());
} }
self.persist_settings_if_changed(); self.persist_settings_if_changed();
} }
_ => self.command_line.push_error(&format!( _ => self.command_line.push_error(crate::tf!(
"{}: expected 0 or 1.", "{}: expected 0 or 1.",
if is_add { "PICKADD" } else { "PICKDRAG" } if is_add { "PICKADD" } else { "PICKDRAG" }
)), ).as_ref()),
} }
} }
} }
@ -1586,9 +1586,9 @@ impl OpenCADStudio {
// prompt for a new value on the next line instead // prompt for a new value on the next line instead
// of only echoing the current one. Enter keeps it. // of only echoing the current one. Enter keeps it.
let current = msg.split('=').nth(1).map(str::trim).unwrap_or(""); let current = msg.split('=').nth(1).map(str::trim).unwrap_or("");
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Enter new value for {name} <{current}>:" "Enter new value for {name} <{current}>:"
)); ).as_ref());
self.pending_setvar = Some(name.clone()); self.pending_setvar = Some(name.clone());
} }
} }
@ -1667,15 +1667,15 @@ impl OpenCADStudio {
} }
}; };
self.command_line self.command_line
.push_output("FINDNONPURGEABLE: named objects in use (not purgeable):"); .push_output(crate::t!("FINDNONPURGEABLE: named objects in use (not purgeable):").as_ref());
self.command_line self.command_line
.push_output(&format!(" Layers: {}", fmt(layers))); .push_output(crate::tf!(" Layers: {}", fmt(layers)).as_ref());
self.command_line self.command_line
.push_output(&format!(" Linetypes: {}", fmt(linetypes))); .push_output(crate::tf!(" Linetypes: {}", fmt(linetypes)).as_ref());
self.command_line self.command_line
.push_output(&format!(" Text styles: {}", fmt(styles))); .push_output(crate::tf!(" Text styles: {}", fmt(styles)).as_ref());
self.command_line self.command_line
.push_output(&format!(" Blocks: {}", fmt(blocks))); .push_output(crate::tf!(" Blocks: {}", fmt(blocks)).as_ref());
} }
// ── AUDIT — report drawing-database integrity issues ───────── // ── AUDIT — report drawing-database integrity issues ─────────
@ -1703,24 +1703,24 @@ impl OpenCADStudio {
} }
} }
self.command_line self.command_line
.push_output(&format!("AUDIT: scanned {total} object(s).")); .push_output(crate::tf!("AUDIT: scanned {total} object(s).").as_ref());
if undefined_layers.is_empty() && undefined_blocks.is_empty() { if undefined_layers.is_empty() && undefined_blocks.is_empty() {
self.command_line.push_output("AUDIT: no issues found."); self.command_line.push_output(crate::t!("AUDIT: no issues found.").as_ref());
} else { } else {
if !undefined_layers.is_empty() { if !undefined_layers.is_empty() {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"AUDIT: reference(s) to undefined layer(s): {}", "AUDIT: reference(s) to undefined layer(s): {}",
undefined_layers.into_iter().collect::<Vec<_>>().join(", ") undefined_layers.into_iter().collect::<Vec<_>>().join(", ")
)); ).as_ref());
} }
if !undefined_blocks.is_empty() { if !undefined_blocks.is_empty() {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"AUDIT: reference(s) to undefined block(s): {}", "AUDIT: reference(s) to undefined block(s): {}",
undefined_blocks.into_iter().collect::<Vec<_>>().join(", ") undefined_blocks.into_iter().collect::<Vec<_>>().join(", ")
)); ).as_ref());
} }
self.command_line self.command_line
.push_info("AUDIT: report only — no automatic repair performed."); .push_info(crate::t!("AUDIT: report only — no automatic repair performed.").as_ref());
} }
} }
@ -1744,7 +1744,7 @@ impl OpenCADStudio {
if type_str.is_empty() || old_name.is_empty() || new_name.is_empty() { if type_str.is_empty() || old_name.is_empty() || new_name.is_empty() {
self.command_line.push_info( self.command_line.push_info(
"Usage: RENAME <type> <old> <new> (types: LAYER BLOCK STYLE DIMSTYLE LINETYPE UCS VIEW)" crate::t!("Usage: RENAME <type> <old> <new> (types: LAYER BLOCK STYLE DIMSTYLE LINETYPE UCS VIEW)").as_ref()
); );
} else { } else {
// Snapshot BEFORE mutating (undo must restore the old // Snapshot BEFORE mutating (undo must restore the old
@ -1789,21 +1789,21 @@ impl OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("RENAME: '{}' → '{}'.", old_name, new_name)); .push_output(crate::tf!("RENAME: '{}' → '{}'.", old_name, new_name).as_ref());
} else { } else {
self.discard_last_undo_entry(i); self.discard_last_undo_entry(i);
if !known { if !known {
self.command_line.push_error(&format!("RENAME: unknown type '{}'. Use LAYER BLOCK STYLE DIMSTYLE LINETYPE UCS VIEW", type_str)); self.command_line.push_error(crate::tf!("RENAME: unknown type '{}'. Use LAYER BLOCK STYLE DIMSTYLE LINETYPE UCS VIEW", type_str).as_ref());
} else if type_str == "BLOCK" { } else if type_str == "BLOCK" {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"RENAME: could not rename block '{}' — missing/anonymous/xref, or '{}' is invalid or taken.", "RENAME: could not rename block '{}' — missing/anonymous/xref, or '{}' is invalid or taken.",
old_name, new_name old_name, new_name
)); ).as_ref());
} else { } else {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"RENAME: could not rename '{}' in {} — not found or protected, or '{}' is invalid or taken.", "RENAME: could not rename '{}' in {} — not found or protected, or '{}' is invalid or taken.",
old_name, type_str, new_name old_name, type_str, new_name
)); ).as_ref());
} }
} }
} }
@ -1825,17 +1825,17 @@ impl OpenCADStudio {
if name_arg.is_empty() { if name_arg.is_empty() {
let cur = &self.tabs[i].scene.document.header.current_layer_name; let cur = &self.tabs[i].scene.document.header.current_layer_name;
self.command_line self.command_line
.push_output(&format!("CLAYER = \"{cur}\"")); .push_output(crate::tf!("CLAYER = \"{cur}\"").as_ref());
} else { } else {
if self.tabs[i].scene.document.layers.contains(name_arg) { if self.tabs[i].scene.document.layers.contains(name_arg) {
self.tabs[i].scene.document.header.current_layer_name = self.tabs[i].scene.document.header.current_layer_name =
name_arg.to_string(); name_arg.to_string();
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("CLAYER set to \"{name_arg}\"")); .push_output(crate::tf!("CLAYER set to \"{name_arg}\"").as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("CLAYER: layer '{}' not found.", name_arg)); .push_error(crate::tf!("CLAYER: layer '{}' not found.", name_arg).as_ref());
} }
} }
} }
@ -1851,16 +1851,16 @@ impl OpenCADStudio {
if name_arg.is_empty() { if name_arg.is_empty() {
let cur = &self.tabs[i].scene.document.header.current_dimstyle_name; let cur = &self.tabs[i].scene.document.header.current_dimstyle_name;
self.command_line self.command_line
.push_output(&format!("CDIMSTY = \"{cur}\"")); .push_output(crate::tf!("CDIMSTY = \"{cur}\"").as_ref());
} else { } else {
if self.tabs[i].scene.document.dim_styles.contains(&name_arg) { if self.tabs[i].scene.document.dim_styles.contains(&name_arg) {
self.tabs[i].scene.document.header.current_dimstyle_name = name_arg.clone(); self.tabs[i].scene.document.header.current_dimstyle_name = name_arg.clone();
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("Active dim style set to \"{name_arg}\"")); .push_output(crate::tf!("Active dim style set to \"{name_arg}\"").as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("CDIMSTY: dim style '{}' not found.", name_arg)); .push_error(crate::tf!("CDIMSTY: dim style '{}' not found.", name_arg).as_ref());
} }
} }
} }
@ -1874,20 +1874,20 @@ impl OpenCADStudio {
let val_str = cmd.trim_start_matches("LTSCALE").trim(); let val_str = cmd.trim_start_matches("LTSCALE").trim();
if val_str.is_empty() { if val_str.is_empty() {
let v = self.tabs[i].scene.document.header.linetype_scale; let v = self.tabs[i].scene.document.header.linetype_scale;
self.command_line.push_output(&format!("LTSCALE = {v:.4}")); self.command_line.push_output(crate::tf!("LTSCALE = {v:.4}").as_ref());
} else if let Ok(v) = val_str.parse::<f64>() { } else if let Ok(v) = val_str.parse::<f64>() {
if v > 0.0 { if v > 0.0 {
self.push_undo_snapshot(i, "LTSCALE"); self.push_undo_snapshot(i, "LTSCALE");
self.tabs[i].scene.document.header.linetype_scale = v; self.tabs[i].scene.document.header.linetype_scale = v;
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("LTSCALE set to {v:.4}")); .push_output(crate::tf!("LTSCALE set to {v:.4}").as_ref());
} else { } else {
self.command_line self.command_line
.push_error("LTSCALE: value must be positive."); .push_error(crate::t!("LTSCALE: value must be positive.").as_ref());
} }
} else { } else {
self.command_line.push_error("Usage: LTSCALE [value]"); self.command_line.push_error(crate::t!("Usage: LTSCALE [value]").as_ref());
} }
} }
"PDMODE" => { "PDMODE" => {
@ -1903,14 +1903,14 @@ impl OpenCADStudio {
let val_str = cmd.trim_start_matches("PDMODE").trim(); let val_str = cmd.trim_start_matches("PDMODE").trim();
if val_str.is_empty() { if val_str.is_empty() {
let v = self.tabs[i].scene.document.header.point_display_mode; let v = self.tabs[i].scene.document.header.point_display_mode;
self.command_line.push_output(&format!("PDMODE = {v}")); self.command_line.push_output(crate::tf!("PDMODE = {v}").as_ref());
} else if let Ok(v) = val_str.parse::<i16>() { } else if let Ok(v) = val_str.parse::<i16>() {
self.push_undo_snapshot(i, "PDMODE"); self.push_undo_snapshot(i, "PDMODE");
self.tabs[i].scene.document.header.point_display_mode = v; self.tabs[i].scene.document.header.point_display_mode = v;
// Point glyphs are built at tessellation time — rebuild them. // Point glyphs are built at tessellation time — rebuild them.
self.tabs[i].scene.invalidate_point_dependencies(); self.tabs[i].scene.invalidate_point_dependencies();
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!("PDMODE set to {v}")); self.command_line.push_output(crate::tf!("PDMODE set to {v}").as_ref());
} else { } else {
self.command_line.push_error( self.command_line.push_error(
"Usage: PDMODE [value] (0=dot 1=none 2=+ 3=x 4=tick; +32 circle, +64 square)", "Usage: PDMODE [value] (0=dot 1=none 2=+ 3=x 4=tick; +32 circle, +64 square)",
@ -1922,17 +1922,17 @@ impl OpenCADStudio {
if val_str.is_empty() { if val_str.is_empty() {
let v = if self.texteditmode { 1 } else { 0 }; let v = if self.texteditmode { 1 } else { 0 };
self.command_line self.command_line
.push_output(&format!("TEXTEDITMODE = {v}")); .push_output(crate::tf!("TEXTEDITMODE = {v}").as_ref());
} else if let Some(v) = } else if let Some(v) =
crate::modules::annotate::textedit::parse_texteditmode(&val_str) crate::modules::annotate::textedit::parse_texteditmode(&val_str)
{ {
self.texteditmode = v; self.texteditmode = v;
let n = if v { 1 } else { 0 }; let n = if v { 1 } else { 0 };
self.command_line self.command_line
.push_output(&format!("TEXTEDITMODE set to {n}")); .push_output(crate::tf!("TEXTEDITMODE set to {n}").as_ref());
} else { } else {
self.command_line self.command_line
.push_error("Requires 0 OR 1 OR MULTIPLE OR SINGLE"); .push_error(crate::t!("Requires 0 OR 1 OR MULTIPLE OR SINGLE").as_ref());
} }
} }
"ISAVEBAK" => { "ISAVEBAK" => {
@ -1946,19 +1946,19 @@ impl OpenCADStudio {
match cmd.trim_start_matches("ISAVEBAK").trim() { match cmd.trim_start_matches("ISAVEBAK").trim() {
"" => { "" => {
let v = if self.backup_on_save { 1 } else { 0 }; let v = if self.backup_on_save { 1 } else { 0 };
self.command_line.push_output(&format!("ISAVEBAK = {v}")); self.command_line.push_output(crate::tf!("ISAVEBAK = {v}").as_ref());
} }
"0" => { "0" => {
self.backup_on_save = false; self.backup_on_save = false;
self.persist_settings_if_changed(); self.persist_settings_if_changed();
self.command_line.push_output("ISAVEBAK set to 0"); self.command_line.push_output(crate::t!("ISAVEBAK set to 0").as_ref());
} }
"1" => { "1" => {
self.backup_on_save = true; self.backup_on_save = true;
self.persist_settings_if_changed(); self.persist_settings_if_changed();
self.command_line.push_output("ISAVEBAK set to 1"); self.command_line.push_output(crate::t!("ISAVEBAK set to 1").as_ref());
} }
_ => self.command_line.push_error("Requires 0 or 1"), _ => self.command_line.push_error(crate::t!("Requires 0 or 1").as_ref()),
} }
} }
"FILEASSOC" => { "FILEASSOC" => {
@ -1974,7 +1974,7 @@ impl OpenCADStudio {
match cmd.trim_start_matches("FILEASSOC").trim() { match cmd.trim_start_matches("FILEASSOC").trim() {
"" => { "" => {
let v = if self.file_assoc_enabled { 1 } else { 0 }; let v = if self.file_assoc_enabled { 1 } else { 0 };
self.command_line.push_output(&format!("FILEASSOC = {v}")); self.command_line.push_output(crate::tf!("FILEASSOC = {v}").as_ref());
} }
"1" => { "1" => {
self.file_assoc_enabled = true; self.file_assoc_enabled = true;
@ -1985,7 +1985,7 @@ impl OpenCADStudio {
), ),
Err(e) => self Err(e) => self
.command_line .command_line
.push_error(&format!("FILEASSOC: registration failed: {e}")), .push_error(crate::tf!("FILEASSOC: registration failed: {e}").as_ref()),
} }
} }
"0" => { "0" => {
@ -1994,13 +1994,13 @@ impl OpenCADStudio {
match crate::io::file_association::unregister_handler() { match crate::io::file_association::unregister_handler() {
Ok(()) => self Ok(()) => self
.command_line .command_line
.push_output("FILEASSOC set to 0 — unregistered as a file handler"), .push_output(crate::t!("FILEASSOC set to 0 — unregistered as a file handler").as_ref()),
Err(e) => self Err(e) => self
.command_line .command_line
.push_error(&format!("FILEASSOC: unregister failed: {e}")), .push_error(crate::tf!("FILEASSOC: unregister failed: {e}").as_ref()),
} }
} }
_ => self.command_line.push_error("Requires 0 or 1"), _ => self.command_line.push_error(crate::t!("Requires 0 or 1").as_ref()),
} }
} }
"SAVETIME" => { "SAVETIME" => {
@ -2016,7 +2016,7 @@ impl OpenCADStudio {
let arg = cmd.trim_start_matches("SAVETIME").trim(); let arg = cmd.trim_start_matches("SAVETIME").trim();
if arg.is_empty() { if arg.is_empty() {
self.command_line self.command_line
.push_output(&format!("SAVETIME = {}", self.savetime_min)); .push_output(crate::tf!("SAVETIME = {}", self.savetime_min).as_ref());
return Some(Task::none()); return Some(Task::none());
} }
match arg.parse::<i32>() { match arg.parse::<i32>() {
@ -2032,7 +2032,7 @@ impl OpenCADStudio {
} }
_ => self _ => self
.command_line .command_line
.push_error("Requires a non-negative number of minutes (0 = off)"), .push_error(crate::t!("Requires a non-negative number of minutes (0 = off)").as_ref()),
} }
} }
"PDSIZE" => { "PDSIZE" => {
@ -2048,14 +2048,14 @@ impl OpenCADStudio {
let val_str = cmd.trim_start_matches("PDSIZE").trim(); let val_str = cmd.trim_start_matches("PDSIZE").trim();
if val_str.is_empty() { if val_str.is_empty() {
let v = self.tabs[i].scene.document.header.point_display_size; let v = self.tabs[i].scene.document.header.point_display_size;
self.command_line.push_output(&format!("PDSIZE = {v:.4}")); self.command_line.push_output(crate::tf!("PDSIZE = {v:.4}").as_ref());
} else if let Ok(v) = val_str.parse::<f64>() { } else if let Ok(v) = val_str.parse::<f64>() {
self.push_undo_snapshot(i, "PDSIZE"); self.push_undo_snapshot(i, "PDSIZE");
self.tabs[i].scene.document.header.point_display_size = v; self.tabs[i].scene.document.header.point_display_size = v;
self.tabs[i].scene.invalidate_point_dependencies(); self.tabs[i].scene.invalidate_point_dependencies();
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("PDSIZE set to {v:.4}")); .push_output(crate::tf!("PDSIZE set to {v:.4}").as_ref());
} else { } else {
self.command_line.push_error( self.command_line.push_error(
"Usage: PDSIZE [value] (>0 absolute size, <0 percent of viewport, 0 default)", "Usage: PDSIZE [value] (>0 absolute size, <0 percent of viewport, 0 default)",
@ -2087,19 +2087,19 @@ impl OpenCADStudio {
_ => Err(()), _ => Err(()),
}; };
match parsed { match parsed {
Err(_) => self.command_line.push_error("Usage: LWDISPLAY [ON|OFF]"), Err(_) => self.command_line.push_error(crate::t!("Usage: LWDISPLAY [ON|OFF]").as_ref()),
Ok(Some(v)) => { Ok(Some(v)) => {
self.push_undo_snapshot(i, "LWDISPLAY"); self.push_undo_snapshot(i, "LWDISPLAY");
self.tabs[i].scene.document.header.lineweight_display = v; self.tabs[i].scene.document.header.lineweight_display = v;
// No retessellate — the wire shader honours the flag via uniforms. // No retessellate — the wire shader honours the flag via uniforms.
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("LWDISPLAY {}", if v { "ON" } else { "OFF" })); .push_output(crate::tf!("LWDISPLAY {}", if v { "ON" } else { "OFF" }).as_ref());
} }
Ok(None) => { Ok(None) => {
let v = self.tabs[i].scene.document.header.lineweight_display; let v = self.tabs[i].scene.document.header.lineweight_display;
self.command_line self.command_line
.push_output(&format!("LWDISPLAY = {}", if v { "ON" } else { "OFF" })); .push_output(crate::tf!("LWDISPLAY = {}", if v { "ON" } else { "OFF" }).as_ref());
} }
} }
} }
@ -2121,7 +2121,7 @@ impl OpenCADStudio {
.header .header
.current_entity_linetype_scale; .current_entity_linetype_scale;
self.command_line self.command_line
.push_output(&format!("CELTSCALE = {v:.4}")); .push_output(crate::tf!("CELTSCALE = {v:.4}").as_ref());
} else if let Ok(v) = val_str.parse::<f64>() { } else if let Ok(v) = val_str.parse::<f64>() {
if v > 0.0 { if v > 0.0 {
self.tabs[i] self.tabs[i]
@ -2131,13 +2131,13 @@ impl OpenCADStudio {
.current_entity_linetype_scale = v; .current_entity_linetype_scale = v;
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("CELTSCALE set to {v:.4}")); .push_output(crate::tf!("CELTSCALE set to {v:.4}").as_ref());
} else { } else {
self.command_line self.command_line
.push_error("CELTSCALE: value must be positive."); .push_error(crate::t!("CELTSCALE: value must be positive.").as_ref());
} }
} else { } else {
self.command_line.push_error("Usage: CELTSCALE [value]"); self.command_line.push_error(crate::t!("Usage: CELTSCALE [value]").as_ref());
} }
} }
@ -2166,7 +2166,7 @@ impl OpenCADStudio {
.collect(); .collect();
if selected_handles.is_empty() { if selected_handles.is_empty() {
self.command_line self.command_line
.push_error("SCALETEXT: select Text/MText entities first."); .push_error(crate::t!("SCALETEXT: select Text/MText entities first.").as_ref());
} else { } else {
let (use_absolute, value) = match ( let (use_absolute, value) = match (
parts.first().map(|s| s.to_uppercase()).as_deref(), parts.first().map(|s| s.to_uppercase()).as_deref(),
@ -2179,7 +2179,7 @@ impl OpenCADStudio {
if let Some(val) = value { if let Some(val) = value {
if val <= 0.0 { if val <= 0.0 {
self.command_line self.command_line
.push_error("SCALETEXT: value must be positive."); .push_error(crate::t!("SCALETEXT: value must be positive.").as_ref());
} else { } else {
self.push_undo_snapshot(i, "SCALETEXT"); self.push_undo_snapshot(i, "SCALETEXT");
let mut count = 0usize; let mut count = 0usize;
@ -2206,17 +2206,17 @@ impl OpenCADStudio {
} }
if count > 0 { if count > 0 {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"SCALETEXT: scaled {count} text entity(ies)." "SCALETEXT: scaled {count} text entity(ies)."
)); ).as_ref());
} else { } else {
self.command_line self.command_line
.push_error("SCALETEXT: no Text/MText in selection."); .push_error(crate::t!("SCALETEXT: no Text/MText in selection.").as_ref());
} }
} }
} else { } else {
self.command_line self.command_line
.push_info("Usage: SCALETEXT <factor> or SCALETEXT H <height>"); .push_info(crate::t!("Usage: SCALETEXT <factor> or SCALETEXT H <height>").as_ref());
} }
} }
} }

View file

@ -4,7 +4,7 @@ impl OpenCADStudio {
pub(super) fn dispatch_view(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> { pub(super) fn dispatch_view(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
match cmd { match cmd {
"DONATE" => { "DONATE" => {
self.command_line.push_info("Opening Patreon page..."); self.command_line.push_info(crate::t!("Opening Patreon page...").as_ref());
return Some(crate::sys::open_url( return Some(crate::sys::open_url(
"https://patreon.com/HakanSeven12", "https://patreon.com/HakanSeven12",
self.main_window, self.main_window,
@ -12,7 +12,7 @@ impl OpenCADStudio {
} }
"WEBVERSION" => { "WEBVERSION" => {
self.command_line.push_info("Opening OCS Web..."); self.command_line.push_info(crate::t!("Opening OCS Web...").as_ref());
return Some(crate::sys::open_url( return Some(crate::sys::open_url(
"https://hakanseven12.github.io/OpenCADStudio/", "https://hakanseven12.github.io/OpenCADStudio/",
self.main_window, self.main_window,
@ -21,7 +21,7 @@ impl OpenCADStudio {
"HELP" => { "HELP" => {
self.command_line self.command_line
.push_info("Opening OCS Discussions for help and questions..."); .push_info(crate::t!("Opening OCS Discussions for help and questions...").as_ref());
return Some(crate::sys::open_url( return Some(crate::sys::open_url(
"https://github.com/HakanSeven12/OpenCADStudio/discussions", "https://github.com/HakanSeven12/OpenCADStudio/discussions",
self.main_window, self.main_window,
@ -41,78 +41,78 @@ impl OpenCADStudio {
.map(|p| p.display().to_string()) .map(|p| p.display().to_string())
.unwrap_or_else(|| "(unsaved)".to_string()); .unwrap_or_else(|| "(unsaved)".to_string());
self.command_line self.command_line
.push_output(&format!("Drawing: {}", path_label)); .push_output(crate::tf!("Drawing: {}", path_label).as_ref());
self.command_line self.command_line
.push_output(&format!(" Created (Julian): {:.6}", h.create_date_julian)); .push_output(crate::tf!(" Created (Julian): {:.6}", h.create_date_julian).as_ref());
self.command_line self.command_line
.push_output(&format!(" Updated (Julian): {:.6}", h.update_date_julian)); .push_output(crate::tf!(" Updated (Julian): {:.6}", h.update_date_julian).as_ref());
self.command_line self.command_line
.push_output(&format!(" Total edit time: {:.4}", h.total_editing_time)); .push_output(crate::tf!(" Total edit time: {:.4}", h.total_editing_time).as_ref());
self.command_line self.command_line
.push_output(&format!(" User elapsed: {:.4}", h.user_elapsed_time)); .push_output(crate::tf!(" User elapsed: {:.4}", h.user_elapsed_time).as_ref());
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" Last saved by: {}", " Last saved by: {}",
if h.last_saved_by.is_empty() { if h.last_saved_by.is_empty() {
"(unknown)" "(unknown)"
} else { } else {
&h.last_saved_by &h.last_saved_by
} }
)); ).as_ref());
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" Fingerprint GUID: {}", " Fingerprint GUID: {}",
if h.fingerprint_guid.is_empty() { if h.fingerprint_guid.is_empty() {
"(none)" "(none)"
} else { } else {
&h.fingerprint_guid &h.fingerprint_guid
} }
)); ).as_ref());
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" Version GUID: {}", " Version GUID: {}",
if h.version_guid.is_empty() { if h.version_guid.is_empty() {
"(none)" "(none)"
} else { } else {
&h.version_guid &h.version_guid
} }
)); ).as_ref());
self.command_line self.command_line
.push_output(&format!(" Code page: {}", h.code_page)); .push_output(crate::tf!(" Code page: {}", h.code_page).as_ref());
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" Menu name: {}", " Menu name: {}",
if h.menu_name.is_empty() { if h.menu_name.is_empty() {
"(none)" "(none)"
} else { } else {
&h.menu_name &h.menu_name
} }
)); ).as_ref());
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" Hyperlink base: {}", " Hyperlink base: {}",
if h.hyperlink_base.is_empty() { if h.hyperlink_base.is_empty() {
"(none)" "(none)"
} else { } else {
&h.hyperlink_base &h.hyperlink_base
} }
)); ).as_ref());
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" Project name: {}", " Project name: {}",
if h.project_name.is_empty() { if h.project_name.is_empty() {
"(none)" "(none)"
} else { } else {
&h.project_name &h.project_name
} }
)); ).as_ref());
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" Stylesheet: {}", " Stylesheet: {}",
if h.stylesheet.is_empty() { if h.stylesheet.is_empty() {
"(none)" "(none)"
} else { } else {
&h.stylesheet &h.stylesheet
} }
)); ).as_ref());
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" Required versions: {:#018x}", " Required versions: {:#018x}",
h.required_versions h.required_versions
)); ).as_ref());
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" Measurement: {} ({})", " Measurement: {} ({})",
h.measurement, h.measurement,
if h.measurement == 1 { if h.measurement == 1 {
@ -120,23 +120,23 @@ impl OpenCADStudio {
} else { } else {
"Imperial" "Imperial"
} }
)); ).as_ref());
self.command_line self.command_line
.push_output(&format!(" Proxy graphics: {}", h.proxy_graphics)); .push_output(crate::tf!(" Proxy graphics: {}", h.proxy_graphics).as_ref());
self.command_line self.command_line
.push_output(&format!(" Tree depth: {}", h.tree_depth)); .push_output(crate::tf!(" Tree depth: {}", h.tree_depth).as_ref());
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" User vars (int): {} {} {} {} {}", " User vars (int): {} {} {} {} {}",
h.user_int1, h.user_int2, h.user_int3, h.user_int4, h.user_int5 h.user_int1, h.user_int2, h.user_int3, h.user_int4, h.user_int5
)); ).as_ref());
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" User vars (real): {:.6} {:.6} {:.6} {:.6} {:.6}", " User vars (real): {:.6} {:.6} {:.6} {:.6} {:.6}",
h.user_real1, h.user_real2, h.user_real3, h.user_real4, h.user_real5 h.user_real1, h.user_real2, h.user_real3, h.user_real4, h.user_real5
)); ).as_ref());
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" User timer: {}", " User timer: {}",
if h.user_timer { "On" } else { "Off" } if h.user_timer { "On" } else { "Off" }
)); ).as_ref());
} }
// Edit a USERI1..USERI5 / USERR1..USERR5 slot. Lets the user // Edit a USERI1..USERI5 / USERR1..USERR5 slot. Lets the user
@ -174,9 +174,9 @@ impl OpenCADStudio {
_ => h.user_real5 = val, _ => h.user_real5 = val,
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!("USERR{n} = {val}")); self.command_line.push_output(crate::tf!("USERR{n} = {val}").as_ref());
} else { } else {
self.command_line.push_info("Usage: USERR <1-5> <real>"); self.command_line.push_info(crate::t!("Usage: USERR <1-5> <real>").as_ref());
} }
} }
(Some(n @ 1..=5), v, false) => { (Some(n @ 1..=5), v, false) => {
@ -189,14 +189,14 @@ impl OpenCADStudio {
_ => h.user_int5 = val, _ => h.user_int5 = val,
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!("USERI{n} = {val}")); self.command_line.push_output(crate::tf!("USERI{n} = {val}").as_ref());
} else { } else {
self.command_line.push_info("Usage: USERI <1-5> <integer>"); self.command_line.push_info(crate::t!("Usage: USERI <1-5> <integer>").as_ref());
} }
} }
_ => self _ => self
.command_line .command_line
.push_info("Usage: USERI <1-5> <int> | USERR <1-5> <real>"), .push_info(crate::t!("Usage: USERI <1-5> <int> | USERR <1-5> <real>").as_ref()),
} }
} }
@ -213,7 +213,7 @@ impl OpenCADStudio {
"https://github.com/HakanSeven12/OpenCADStudio/issues/new?body={}", "https://github.com/HakanSeven12/OpenCADStudio/issues/new?body={}",
crate::sys::percent_encode(&body) crate::sys::percent_encode(&body)
); );
self.command_line.push_info("Opening feedback page..."); self.command_line.push_info(crate::t!("Opening feedback page...").as_ref());
return Some(crate::sys::open_url(&url, self.main_window)); return Some(crate::sys::open_url(&url, self.main_window));
} }
@ -226,7 +226,7 @@ impl OpenCADStudio {
} }
"CHANGELOG" => { "CHANGELOG" => {
self.command_line.push_info("Opening release notes..."); self.command_line.push_info(crate::t!("Opening release notes...").as_ref());
return Some(crate::sys::open_url( return Some(crate::sys::open_url(
"https://github.com/HakanSeven12/OpenCADStudio/releases", "https://github.com/HakanSeven12/OpenCADStudio/releases",
self.main_window, self.main_window,
@ -266,12 +266,12 @@ impl OpenCADStudio {
let text: String = keys.iter().map(|(k, v)| format!("{k} {v}\n")).collect(); let text: String = keys.iter().map(|(k, v)| format!("{k} {v}\n")).collect();
let count = self.shortcut_overrides.len(); let count = self.shortcut_overrides.len();
match std::fs::write(path, text) { match std::fs::write(path, text) {
Ok(()) => self.command_line.push_output(&format!( Ok(()) => self.command_line.push_output(crate::tf!(
"CUIEXPORT: wrote {count} shortcut(s) to \"{path}\"." "CUIEXPORT: wrote {count} shortcut(s) to \"{path}\"."
)), ).as_ref()),
Err(e) => self Err(e) => self
.command_line .command_line
.push_error(&format!("CUIEXPORT: cannot write \"{path}\": {e}")), .push_error(crate::tf!("CUIEXPORT: cannot write \"{path}\": {e}").as_ref()),
} }
} }
@ -308,13 +308,13 @@ impl OpenCADStudio {
n += 1; n += 1;
} }
} }
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"CUIIMPORT: loaded {n} shortcut(s) from \"{path}\"." "CUIIMPORT: loaded {n} shortcut(s) from \"{path}\"."
)); ).as_ref());
} }
Err(e) => self Err(e) => self
.command_line .command_line
.push_error(&format!("CUIIMPORT: cannot read \"{path}\": {e}")), .push_error(crate::tf!("CUIIMPORT: cannot read \"{path}\": {e}").as_ref()),
} }
} }
@ -333,28 +333,28 @@ impl OpenCADStudio {
let key = parts.get(1).map(|s| s.to_uppercase()).unwrap_or_default(); let key = parts.get(1).map(|s| s.to_uppercase()).unwrap_or_default();
let cmd_str = parts.get(2).map(|s| s.to_uppercase()).unwrap_or_default(); let cmd_str = parts.get(2).map(|s| s.to_uppercase()).unwrap_or_default();
if key.is_empty() || cmd_str.is_empty() { if key.is_empty() || cmd_str.is_empty() {
self.command_line.push_error("Usage: SHORTCUTS SET <key> <command> e.g. SHORTCUTS SET CTRL+D DIST"); self.command_line.push_error(crate::t!("Usage: SHORTCUTS SET <key> <command> e.g. SHORTCUTS SET CTRL+D DIST").as_ref());
} else { } else {
self.shortcut_overrides.insert(key.clone(), cmd_str.clone()); self.shortcut_overrides.insert(key.clone(), cmd_str.clone());
self.command_line self.command_line
.push_output(&format!("Shortcut set: {key} → {cmd_str}")); .push_output(crate::tf!("Shortcut set: {key} → {cmd_str}").as_ref());
} }
} }
"CLEAR" | "DELETE" | "REMOVE" => { "CLEAR" | "DELETE" | "REMOVE" => {
let key = parts.get(1).map(|s| s.to_uppercase()).unwrap_or_default(); let key = parts.get(1).map(|s| s.to_uppercase()).unwrap_or_default();
if key.is_empty() { if key.is_empty() {
self.command_line.push_error("Usage: SHORTCUTS CLEAR <key>"); self.command_line.push_error(crate::t!("Usage: SHORTCUTS CLEAR <key>").as_ref());
} else if self.shortcut_overrides.remove(&key).is_some() { } else if self.shortcut_overrides.remove(&key).is_some() {
self.command_line self.command_line
.push_output(&format!("Shortcut '{key}' removed.")); .push_output(crate::tf!("Shortcut '{key}' removed.").as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("Shortcut '{key}' not found.")); .push_error(crate::tf!("Shortcut '{key}' not found.").as_ref());
} }
} }
_ => { _ => {
self.command_line self.command_line
.push_info("Usage: SHORTCUTS LIST | SET <key> <cmd> | CLEAR <key>"); .push_info(crate::t!("Usage: SHORTCUTS LIST | SET <key> <cmd> | CLEAR <key>").as_ref());
} }
} }
} }
@ -396,17 +396,17 @@ impl OpenCADStudio {
return Some(Task::none()); return Some(Task::none());
} }
_ => { _ => {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"COLORSCHEME: unknown theme '{}'. Type COLORSCHEME LIST for options.", "COLORSCHEME: unknown theme '{}'. Type COLORSCHEME LIST for options.",
sub sub
)); ).as_ref());
return Some(Task::none()); return Some(Task::none());
} }
}; };
if let Some(t) = theme { if let Some(t) = theme {
let name = format!("{:?}", t); let name = format!("{:?}", t);
self.command_line self.command_line
.push_output(&format!("Color scheme set to '{name}'.")); .push_output(crate::tf!("Color scheme set to '{name}'.").as_ref());
return Some(Task::done(Message::SetTheme(t))); return Some(Task::done(Message::SetTheme(t)));
} }
return Some(Task::none()); return Some(Task::none());
@ -421,7 +421,7 @@ impl OpenCADStudio {
"MVIEW" => { "MVIEW" => {
if self.tabs[i].scene.current_layout == "Model" { if self.tabs[i].scene.current_layout == "Model" {
self.command_line self.command_line
.push_error("MVIEW: switch to a paper space layout first."); .push_error(crate::t!("MVIEW: switch to a paper space layout first.").as_ref());
} else { } else {
use crate::modules::layout::mview::MviewCommand; use crate::modules::layout::mview::MviewCommand;
let scene = &self.tabs[i].scene; let scene = &self.tabs[i].scene;
@ -464,7 +464,7 @@ impl OpenCADStudio {
if sub.is_empty() { if sub.is_empty() {
self.awaiting_vports = true; self.awaiting_vports = true;
self.command_line self.command_line
.push_info("VPORTS Configuration [SIngle/2H/2V/4]:"); .push_info(crate::t!("VPORTS Configuration [SIngle/2H/2V/4]:").as_ref());
return Some(self.focus_cmd_input()); return Some(self.focus_cmd_input());
} }
// Model space: split the tiled viewport layout via pane_grid. // Model space: split the tiled viewport layout via pane_grid.
@ -494,11 +494,11 @@ impl OpenCADStudio {
self.tabs[i].scene.set_model_panes(config); self.tabs[i].scene.set_model_panes(config);
self.tabs[i].scene.camera_generation += 1; self.tabs[i].scene.camera_generation += 1;
self.command_line self.command_line
.push_output(&format!("VPORTS: {n} viewport(s).")); .push_output(crate::tf!("VPORTS: {n} viewport(s).").as_ref());
} }
None => { None => {
self.command_line self.command_line
.push_error("VPORTS: use SINGLE | 2H | 2V | 4."); .push_error(crate::t!("VPORTS: use SINGLE | 2H | 2V | 4.").as_ref());
} }
} }
} else if sub.is_empty() { } else if sub.is_empty() {
@ -532,23 +532,23 @@ impl OpenCADStudio {
}) })
.collect(); .collect();
if viewports.is_empty() { if viewports.is_empty() {
self.command_line.push_info("No viewports. Use MVIEW to create one, or VPORTS 2H / 2V / 4 / SINGLE."); self.command_line.push_info(crate::t!("No viewports. Use MVIEW to create one, or VPORTS 2H / 2V / 4 / SINGLE.").as_ref());
} else { } else {
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"{} viewport(s) in layout \"{}\":", "{} viewport(s) in layout \"{}\":",
viewports.len(), viewports.len(),
scene.current_layout scene.current_layout
)); ).as_ref());
for (id, center, w, h, scale, is_on, locked) in &viewports { for (id, center, w, h, scale, is_on, locked) in &viewports {
let state = match (is_on, locked) { let state = match (is_on, locked) {
(true, true) => "On, Locked", (true, true) => "On, Locked",
(true, false) => "On", (true, false) => "On",
(false, _) => "Off", (false, _) => "Off",
}; };
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
" VP #{id}: {w:.1}×{h:.1} @ ({:.1},{:.1}) scale={scale:.4} [{state}]", " VP #{id}: {w:.1}×{h:.1} @ ({:.1},{:.1}) scale={scale:.4} [{state}]",
center.x, center.y center.x, center.y
)); ).as_ref());
} }
} }
} else { } else {
@ -654,7 +654,7 @@ impl OpenCADStudio {
self.tabs[i].scene.auto_fit_viewport(handle); self.tabs[i].scene.auto_fit_viewport(handle);
} }
Err(e) => { Err(e) => {
self.command_line.push_error(&format!("VPORTS: {e}")); self.command_line.push_error(crate::tf!("VPORTS: {e}").as_ref());
} }
} }
} }
@ -686,11 +686,11 @@ impl OpenCADStudio {
} }
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"VPORTS: created {} viewport(s) [{}].", "VPORTS: created {} viewport(s) [{}].",
rects.len(), rects.len(),
sub sub
)); ).as_ref());
} }
} }
} }
@ -700,10 +700,10 @@ impl OpenCADStudio {
let scene = &self.tabs[i].scene; let scene = &self.tabs[i].scene;
if scene.current_layout == "Model" { if scene.current_layout == "Model" {
self.command_line self.command_line
.push_error("VPLAYER: switch to a paper space layout first."); .push_error(crate::t!("VPLAYER: switch to a paper space layout first.").as_ref());
} else if scene.active_viewport.is_none() { } else if scene.active_viewport.is_none() {
self.command_line self.command_line
.push_error("VPLAYER: enter a viewport first (double-click or MS)."); .push_error(crate::t!("VPLAYER: enter a viewport first (double-click or MS).").as_ref());
} else { } else {
use crate::modules::layout::vplayer::VplayerCommand; use crate::modules::layout::vplayer::VplayerCommand;
let vp_handle = scene.active_viewport.unwrap(); let vp_handle = scene.active_viewport.unwrap();
@ -729,12 +729,12 @@ impl OpenCADStudio {
}; };
if frozen_names.is_empty() { if frozen_names.is_empty() {
self.command_line self.command_line
.push_info("VPLAYER: no frozen layers in active viewport."); .push_info(crate::t!("VPLAYER: no frozen layers in active viewport.").as_ref());
} else { } else {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"VPLAYER: frozen layers: {}", "VPLAYER: frozen layers: {}",
frozen_names.join(", ") frozen_names.join(", ")
)); ).as_ref());
} }
let new_cmd = VplayerCommand::new(vp_handle); let new_cmd = VplayerCommand::new(vp_handle);
self.command_line.push_info(&new_cmd.prompt()); self.command_line.push_info(&new_cmd.prompt());
@ -763,7 +763,7 @@ impl OpenCADStudio {
.collect(); .collect();
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_info(&format!("{cmd}: no text or dimension objects.")); .push_info(crate::tf!("{cmd}: no text or dimension objects.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
self.tabs[i].scene.replace_selection(handles); self.tabs[i].scene.replace_selection(handles);
@ -802,7 +802,7 @@ impl OpenCADStudio {
.collect(); .collect();
if selected.is_empty() { if selected.is_empty() {
self.command_line self.command_line
.push_error("DRAWORDER: select entities first."); .push_error(crate::t!("DRAWORDER: select entities first.").as_ref());
} else { } else {
// Parse relative target handle for ABOVE/UNDER. // Parse relative target handle for ABOVE/UNDER.
let relative_target: Option<(bool, acadrust::Handle)> = match option.as_str() { let relative_target: Option<(bool, acadrust::Handle)> = match option.as_str() {
@ -940,12 +940,12 @@ impl OpenCADStudio {
} }
} }
let rel = if above { "above" } else { "below" }; let rel = if above { "above" } else { "below" };
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"DRAWORDER: moved {} entities {} {:x}.", "DRAWORDER: moved {} entities {} {:x}.",
selected.len(), selected.len(),
rel, rel,
target.value() target.value()
)); ).as_ref());
} else if let Some(to_front) = to_front_opt { } else if let Some(to_front) = to_front_opt {
let (min_eff, max_eff) = fb_baseline.unwrap_or((1, 0)); let (min_eff, max_eff) = fb_baseline.unwrap_or((1, 0));
for (k, h) in selected.iter().enumerate() { for (k, h) in selected.iter().enumerate() {
@ -957,11 +957,11 @@ impl OpenCADStudio {
table.add_entry(*h, acadrust::Handle::new(sort)); table.add_entry(*h, acadrust::Handle::new(sort));
} }
let dir = if to_front { "front" } else { "back" }; let dir = if to_front { "front" } else { "back" };
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"DRAWORDER: moved {} entities to {}.", "DRAWORDER: moved {} entities to {}.",
selected.len(), selected.len(),
dir dir
)); ).as_ref());
} }
} }
// Sort order lives in SortEntitiesTable, which the // Sort order lives in SortEntitiesTable, which the
@ -974,7 +974,7 @@ impl OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
} else { } else {
self.command_line.push_info( self.command_line.push_info(
"Usage: DRAWORDER F|FRONT | B|BACK | A|ABOVE <handle> | U|UNDER <handle>" crate::t!("Usage: DRAWORDER F|FRONT | B|BACK | A|ABOVE <handle> | U|UNDER <handle>").as_ref()
); );
} }
} }
@ -1000,7 +1000,7 @@ impl OpenCADStudio {
Some(acadrust::EntityType::Viewport(vp)) => vp.clone(), Some(acadrust::EntityType::Viewport(vp)) => vp.clone(),
_ => { _ => {
self.command_line self.command_line
.push_error("SYNCPVIEWPORTS: master is not a viewport."); .push_error(crate::t!("SYNCPVIEWPORTS: master is not a viewport.").as_ref());
return Some(Task::none()); return Some(Task::none());
} }
}; };
@ -1023,9 +1023,9 @@ impl OpenCADStudio {
} }
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"SYNCPVIEWPORTS: synced {n} viewport(s) to the master." "SYNCPVIEWPORTS: synced {n} viewport(s) to the master."
)); ).as_ref());
} }
// HIDE — hidden-line view of the active viewport. // HIDE — hidden-line view of the active viewport.
@ -1088,9 +1088,9 @@ impl CadCommand for DrawOrderRefCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
if self.above { if self.above {
"DRAWORDER Select reference object (move selection above):".into() crate::t!("DRAWORDER Select reference object (move selection above):").into_owned()
} else { } else {
"DRAWORDER Select reference object (move selection under):".into() crate::t!("DRAWORDER Select reference object (move selection under):").into_owned()
} }
} }

View file

@ -10,6 +10,7 @@ use crate::ui::{LayerPanel, PropertiesPanel};
use acadrust::tables::Ucs; use acadrust::tables::Ucs;
use acadrust::{CadDocument, EntityType, Handle}; use acadrust::{CadDocument, EntityType, Handle};
use iced; use iced;
use crate::t;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::PathBuf; use std::path::PathBuf;
@ -496,7 +497,13 @@ impl DocumentTab {
.unwrap_or_default() .unwrap_or_default()
.to_string_lossy() .to_string_lossy()
.to_string(), .to_string(),
None => self.tab_title.clone(), None => {
if self.is_start {
t!("Start").into_owned()
} else {
self.tab_title.clone()
}
}
} }
} }
} }

View file

@ -131,9 +131,9 @@ impl OpenCADStudio {
"Replaced 1 occurrence in {}; {remaining} matching object(s) remain.", "Replaced 1 occurrence in {}; {remaining} matching object(s) remain.",
match_label(target) match_label(target)
); );
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"FIND/REPLACE: replaced 1 occurrence of \"{search}\"." "FIND/REPLACE: replaced 1 occurrence of \"{search}\"."
)); ).as_ref());
self.refresh_properties(); self.refresh_properties();
} }
@ -188,9 +188,9 @@ impl OpenCADStudio {
"Replaced {replaced} occurrence(s) in {} object(s).", "Replaced {replaced} occurrence(s) in {} object(s).",
changed.len() changed.len()
); );
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"FIND/REPLACE: replaced {replaced} occurrence(s) of \"{search}\"." "FIND/REPLACE: replaced {replaced} occurrence(s) of \"{search}\"."
)); ).as_ref());
self.refresh_properties(); self.refresh_properties();
} }

View file

@ -360,7 +360,7 @@ pub(super) fn next_group_auto_name(scene: &crate::scene::Scene) -> String {
// ── Entity type labels ───────────────────────────────────────────────────── // ── Entity type labels ─────────────────────────────────────────────────────
pub(super) fn entity_type_label(entity: &acadrust::EntityType) -> String { pub(super) fn entity_type_label(entity: &acadrust::EntityType) -> String {
crate::entities::names::ui_name_or_class(entity) crate::t!(crate::entities::names::ui_name_or_class(entity)).into_owned()
} }
pub(super) fn entity_type_key(entity: &acadrust::EntityType) -> String { pub(super) fn entity_type_key(entity: &acadrust::EntityType) -> String {

View file

@ -1020,7 +1020,7 @@ impl OpenCADStudio {
let available = self.tabs[i].history.undo_stack.len(); let available = self.tabs[i].history.undo_stack.len();
let steps = steps.min(available); let steps = steps.min(available);
if steps == 0 { if steps == 0 {
self.command_line.push_info("Nothing to undo."); self.command_line.push_info(crate::t!("Nothing to undo.").as_ref());
return; return;
} }
@ -1069,7 +1069,7 @@ impl OpenCADStudio {
} }
self.finish_history_apply(i, had_full, layer_panel_changed, &changes); self.finish_history_apply(i, had_full, layer_panel_changed, &changes);
self.command_line self.command_line
.push_output(&format!("Undo: {last_label}")); .push_output(crate::tf!("Undo: {last_label}").as_ref());
} }
pub(super) fn redo_steps(&mut self, steps: usize) { pub(super) fn redo_steps(&mut self, steps: usize) {
@ -1078,7 +1078,7 @@ impl OpenCADStudio {
let available = self.tabs[i].history.redo_stack.len(); let available = self.tabs[i].history.redo_stack.len();
let steps = steps.min(available); let steps = steps.min(available);
if steps == 0 { if steps == 0 {
self.command_line.push_info("Nothing to redo."); self.command_line.push_info(crate::t!("Nothing to redo.").as_ref());
return; return;
} }
@ -1124,7 +1124,7 @@ impl OpenCADStudio {
} }
self.finish_history_apply(i, had_full, layer_panel_changed, &changes); self.finish_history_apply(i, had_full, layer_panel_changed, &changes);
self.command_line self.command_line
.push_output(&format!("Redo: {last_label}")); .push_output(crate::tf!("Redo: {last_label}").as_ref());
} }
} }

View file

@ -3019,7 +3019,7 @@ impl OpenCADStudio {
for (id, res) in crate::plugin::external::load_at_startup(&mut app) { for (id, res) in crate::plugin::external::load_at_startup(&mut app) {
if let Err(e) = res { if let Err(e) = res {
app.command_line app.command_line
.push_error(&format!("Plugin '{id}' failed to load: {e}")); .push_error(crate::tf!("Plugin '{id}' failed to load: {e}").as_ref());
app.plugin_load_errors.insert(id, e); app.plugin_load_errors.insert(id, e);
} }
} }

View file

@ -38,7 +38,7 @@ impl super::OpenCADStudio {
.collect(); .collect();
if handles.len() != 2 { if handles.len() != 2 {
self.command_line self.command_line
.push_error("Boolean: select exactly two solids created this session."); .push_error(crate::t!("Boolean: select exactly two solids created this session.").as_ref());
return Task::none(); return Task::none();
} }
let a = self.tabs[i].scene.solid_models[&handles[0]].clone(); let a = self.tabs[i].scene.solid_models[&handles[0]].clone();
@ -50,7 +50,7 @@ impl super::OpenCADStudio {
}; };
let Some(result) = solid_model::boolean(kind, &a, &b) else { let Some(result) = solid_model::boolean(kind, &a, &b) else {
self.command_line self.command_line
.push_error("Boolean failed — the solids may not overlap."); .push_error(crate::t!("Boolean failed — the solids may not overlap.").as_ref());
return Task::none(); return Task::none();
}; };
@ -87,7 +87,7 @@ impl super::OpenCADStudio {
.collect(); .collect();
if handles.len() != 1 { if handles.len() != 1 {
self.command_line self.command_line
.push_error("SLICE: select exactly one solid created this session."); .push_error(crate::t!("SLICE: select exactly one solid created this session.").as_ref());
return Task::none(); return Task::none();
} }
let solid = self.tabs[i].scene.solid_models[&handles[0]].clone(); let solid = self.tabs[i].scene.solid_models[&handles[0]].clone();
@ -105,7 +105,7 @@ impl super::OpenCADStudio {
} }
if min[0] > max[0] { if min[0] > max[0] {
self.command_line self.command_line
.push_error("SLICE: could not determine the solid's extent."); .push_error(crate::t!("SLICE: could not determine the solid's extent.").as_ref());
return Task::none(); return Task::none();
} }
// Generous margin so the box fully spans the solid in the free axes. // Generous margin so the box fully spans the solid in the free axes.
@ -123,7 +123,7 @@ impl super::OpenCADStudio {
} }
if hi[axis] <= lo[axis] { if hi[axis] <= lo[axis] {
self.command_line self.command_line
.push_error("SLICE: the plane does not cross the solid on the kept side."); .push_error(crate::t!("SLICE: the plane does not cross the solid on the kept side.").as_ref());
return Task::none(); return Task::none();
} }
let center = [ let center = [
@ -134,7 +134,7 @@ impl super::OpenCADStudio {
let halfspace = solid_model::box_solid(center, hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]); let halfspace = solid_model::box_solid(center, hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]);
let Some(result) = solid_model::boolean(Bool::Intersect, &solid, &halfspace) else { let Some(result) = solid_model::boolean(Bool::Intersect, &solid, &halfspace) else {
self.command_line self.command_line
.push_error("SLICE failed — the plane may not cross the solid."); .push_error(crate::t!("SLICE failed — the plane may not cross the solid.").as_ref());
return Task::none(); return Task::none();
}; };
self.push_undo_snapshot(i, "SLICE"); self.push_undo_snapshot(i, "SLICE");
@ -149,10 +149,10 @@ impl super::OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
let ax = ["X", "Y", "Z"][axis]; let ax = ["X", "Y", "Z"][axis];
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"SLICE: cut at {ax}={value}, kept the {} half.", "SLICE: cut at {ax}={value}, kept the {} half.",
if keep_low { "lower" } else { "upper" } if keep_low { "lower" } else { "upper" }
)); ).as_ref());
Task::none() Task::none()
} }
@ -169,7 +169,7 @@ impl super::OpenCADStudio {
.collect(); .collect();
if handles.len() != 2 { if handles.len() != 2 {
self.command_line self.command_line
.push_error("INTERFERE: select exactly two solids created this session."); .push_error(crate::t!("INTERFERE: select exactly two solids created this session.").as_ref());
return Task::none(); return Task::none();
} }
let a = self.tabs[i].scene.solid_models[&handles[0]].clone(); let a = self.tabs[i].scene.solid_models[&handles[0]].clone();
@ -184,11 +184,11 @@ impl super::OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
self.command_line self.command_line
.push_output("INTERFERE: created an interference solid from the overlap."); .push_output(crate::t!("INTERFERE: created an interference solid from the overlap.").as_ref());
} }
None => self None => self
.command_line .command_line
.push_output("INTERFERE: the selected solids do not overlap."), .push_output(crate::t!("INTERFERE: the selected solids do not overlap.").as_ref()),
} }
Task::none() Task::none()
} }
@ -208,7 +208,7 @@ impl super::OpenCADStudio {
.collect(); .collect();
if handles.len() != 1 { if handles.len() != 1 {
self.command_line self.command_line
.push_error("3DROTATE: select exactly one solid created this session."); .push_error(crate::t!("3DROTATE: select exactly one solid created this session.").as_ref());
return Task::none(); return Task::none();
} }
let solid = self.tabs[i].scene.solid_models[&handles[0]].clone(); let solid = self.tabs[i].scene.solid_models[&handles[0]].clone();
@ -225,7 +225,7 @@ impl super::OpenCADStudio {
} }
if min[0] > max[0] { if min[0] > max[0] {
self.command_line self.command_line
.push_error("3DROTATE: could not determine the solid's extent."); .push_error(crate::t!("3DROTATE: could not determine the solid's extent.").as_ref());
return Task::none(); return Task::none();
} }
let origin = Point3::new( let origin = Point3::new(
@ -250,10 +250,10 @@ impl super::OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"3DROTATE: rotated {angle_deg}° about the {} axis.", "3DROTATE: rotated {angle_deg}° about the {} axis.",
["X", "Y", "Z"][axis] ["X", "Y", "Z"][axis]
)); ).as_ref());
Task::none() Task::none()
} }
@ -280,7 +280,7 @@ impl super::OpenCADStudio {
}); });
let Some((handle, pts, closed)) = found else { let Some((handle, pts, closed)) = found else {
self.command_line self.command_line
.push_error("POLYSOLID: select a polyline first."); .push_error(crate::t!("POLYSOLID: select a polyline first.").as_ref());
return Task::none(); return Task::none();
}; };
let mut segs: Vec<([f64; 2], [f64; 2])> = pts.windows(2).map(|w| (w[0], w[1])).collect(); let mut segs: Vec<([f64; 2], [f64; 2])> = pts.windows(2).map(|w| (w[0], w[1])).collect();
@ -308,7 +308,7 @@ impl super::OpenCADStudio {
} }
let Some(result) = acc else { let Some(result) = acc else {
self.command_line self.command_line
.push_error("POLYSOLID: the polyline has no usable segments."); .push_error(crate::t!("POLYSOLID: the polyline has no usable segments.").as_ref());
return Task::none(); return Task::none();
}; };
self.push_undo_snapshot(i, "POLYSOLID"); self.push_undo_snapshot(i, "POLYSOLID");
@ -322,9 +322,9 @@ impl super::OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"POLYSOLID: built a wall solid from {used} segment(s) (width {width}, height {height})." "POLYSOLID: built a wall solid from {used} segment(s) (width {width}, height {height})."
)); ).as_ref());
Task::none() Task::none()
} }
@ -344,7 +344,7 @@ impl super::OpenCADStudio {
.collect(); .collect();
if handles.len() != 1 { if handles.len() != 1 {
self.command_line self.command_line
.push_error("3DMIRROR: select exactly one solid created this session."); .push_error(crate::t!("3DMIRROR: select exactly one solid created this session.").as_ref());
return Task::none(); return Task::none();
} }
let solid = self.tabs[i].scene.solid_models[&handles[0]].clone(); let solid = self.tabs[i].scene.solid_models[&handles[0]].clone();
@ -361,7 +361,7 @@ impl super::OpenCADStudio {
} }
if min[0] > max[0] { if min[0] > max[0] {
self.command_line self.command_line
.push_error("3DMIRROR: could not determine the solid's extent."); .push_error(crate::t!("3DMIRROR: could not determine the solid's extent.").as_ref());
return Task::none(); return Task::none();
} }
let c = TVec3::new( let c = TVec3::new(
@ -390,10 +390,10 @@ impl super::OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"3DMIRROR: added a mirror across the {} plane.", "3DMIRROR: added a mirror across the {} plane.",
["X", "Y", "Z"][axis] ["X", "Y", "Z"][axis]
)); ).as_ref());
Task::none() Task::none()
} }
@ -417,7 +417,7 @@ impl super::OpenCADStudio {
.collect(); .collect();
if handles.len() != 1 { if handles.len() != 1 {
self.command_line self.command_line
.push_error("3DALIGN: select exactly one solid created this session."); .push_error(crate::t!("3DALIGN: select exactly one solid created this session.").as_ref());
return Task::none(); return Task::none();
} }
// Build a right-handed frame (origin + orthonormal axes) from 3 points. // Build a right-handed frame (origin + orthonormal axes) from 3 points.
@ -440,7 +440,7 @@ impl super::OpenCADStudio {
}; };
let (Some(s), Some(d)) = (frame(src), frame(dst)) else { let (Some(s), Some(d)) = (frame(src), frame(dst)) else {
self.command_line self.command_line
.push_error("3DALIGN: each point triple must be non-coincident and non-collinear."); .push_error(crate::t!("3DALIGN: each point triple must be non-coincident and non-collinear.").as_ref());
return Task::none(); return Task::none();
}; };
let a = (d * s.inverse()).to_cols_array(); // column-major [f64; 16] let a = (d * s.inverse()).to_cols_array(); // column-major [f64; 16]
@ -462,7 +462,7 @@ impl super::OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
self.command_line self.command_line
.push_output("3DALIGN: aligned the solid to the destination points."); .push_output(crate::t!("3DALIGN: aligned the solid to the destination points.").as_ref());
Task::none() Task::none()
} }
@ -487,7 +487,7 @@ impl super::OpenCADStudio {
.collect(); .collect();
if handles.len() != 1 { if handles.len() != 1 {
self.command_line self.command_line
.push_error("SECTION: select exactly one solid created this session."); .push_error(crate::t!("SECTION: select exactly one solid created this session.").as_ref());
return Task::none(); return Task::none();
} }
let solid = self.tabs[i].scene.solid_models[&handles[0]].clone(); let solid = self.tabs[i].scene.solid_models[&handles[0]].clone();
@ -504,7 +504,7 @@ impl super::OpenCADStudio {
} }
if min[0] > max[0] { if min[0] > max[0] {
self.command_line self.command_line
.push_error("SECTION: could not determine the solid's extent."); .push_error(crate::t!("SECTION: could not determine the solid's extent.").as_ref());
return Task::none(); return Task::none();
} }
// Margin so the cutting plane fully spans the solid in the free axes. // Margin so the cutting plane fully spans the solid in the free axes.
@ -546,7 +546,7 @@ impl super::OpenCADStudio {
.collect(); .collect();
let Ok(face) = builder::try_attach_plane(&[wire]) else { let Ok(face) = builder::try_attach_plane(&[wire]) else {
self.command_line self.command_line
.push_error("SECTION: could not build the cutting plane."); .push_error(crate::t!("SECTION: could not build the cutting plane.").as_ref());
return Task::none(); return Task::none();
}; };
let shell: Shell = std::iter::once(face).collect(); let shell: Shell = std::iter::once(face).collect();
@ -556,7 +556,7 @@ impl super::OpenCADStudio {
let segs = solid_mesh.extract_interference(&plane_mesh); let segs = solid_mesh.extract_interference(&plane_mesh);
if segs.is_empty() { if segs.is_empty() {
self.command_line self.command_line
.push_output("SECTION: the plane does not cross the solid."); .push_output(crate::t!("SECTION: the plane does not cross the solid.").as_ref());
return Task::none(); return Task::none();
} }
self.push_undo_snapshot(i, "SECTION"); self.push_undo_snapshot(i, "SECTION");
@ -569,11 +569,11 @@ impl super::OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"SECTION: created {} section line(s) at {}={value}.", "SECTION: created {} section line(s) at {}={value}.",
segs.len(), segs.len(),
["X", "Y", "Z"][axis] ["X", "Y", "Z"][axis]
)); ).as_ref());
Task::none() Task::none()
} }
@ -581,7 +581,7 @@ impl super::OpenCADStudio {
#[cfg(not(feature = "solid3d"))] #[cfg(not(feature = "solid3d"))]
pub(super) fn solid_section(&mut self, _axis: usize, _value: f64) -> Task<Message> { pub(super) fn solid_section(&mut self, _axis: usize, _value: f64) -> Task<Message> {
self.command_line self.command_line
.push_error("SECTION: solid modelling is unavailable in this build."); .push_error(crate::t!("SECTION: solid modelling is unavailable in this build.").as_ref());
Task::none() Task::none()
} }
@ -604,7 +604,7 @@ impl super::OpenCADStudio {
let n = sides.max(3); let n = sides.max(3);
if radius <= 0.0 || height <= 0.0 { if radius <= 0.0 || height <= 0.0 {
self.command_line self.command_line
.push_error("PYRAMID: radius and height must be positive."); .push_error(crate::t!("PYRAMID: radius and height must be positive.").as_ref());
return Task::none(); return Task::none();
} }
let corners: Vec<Point3> = (0..n) let corners: Vec<Point3> = (0..n)
@ -642,7 +642,7 @@ impl super::OpenCADStudio {
} }
if faces.len() < n + 1 { if faces.len() < n + 1 {
self.command_line self.command_line
.push_error("PYRAMID: could not build all faces."); .push_error(crate::t!("PYRAMID: could not build all faces.").as_ref());
return Task::none(); return Task::none();
} }
let shell = Shell::from(faces); let shell = Shell::from(faces);
@ -655,12 +655,12 @@ impl super::OpenCADStudio {
} = truck_tess::tessellate_shell(&shell) } = truck_tess::tessellate_shell(&shell)
else { else {
self.command_line self.command_line
.push_error("PYRAMID: tessellation failed."); .push_error(crate::t!("PYRAMID: tessellation failed.").as_ref());
return Task::none(); return Task::none();
}; };
if verts.is_empty() { if verts.is_empty() {
self.command_line self.command_line
.push_error("PYRAMID: tessellation produced no geometry."); .push_error(crate::t!("PYRAMID: tessellation produced no geometry.").as_ref());
return Task::none(); return Task::none();
} }
self.push_undo_snapshot(i, "PYRAMID"); self.push_undo_snapshot(i, "PYRAMID");
@ -682,9 +682,9 @@ impl super::OpenCADStudio {
.insert(new_handle, crate::scene::MeshLodSet::from_single(mesh)); .insert(new_handle, crate::scene::MeshLodSet::from_single(mesh));
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"PYRAMID: created a {n}-sided pyramid (radius {radius}, height {height})." "PYRAMID: created a {n}-sided pyramid (radius {radius}, height {height})."
)); ).as_ref());
Task::none() Task::none()
} }
@ -713,12 +713,12 @@ impl super::OpenCADStudio {
}); });
let Some((handle, fit)) = found else { let Some((handle, fit)) = found else {
self.command_line self.command_line
.push_error("SPLINEFIT: select a polyline to fit a spline through."); .push_error(crate::t!("SPLINEFIT: select a polyline to fit a spline through.").as_ref());
return Task::none(); return Task::none();
}; };
if fit.len() < 3 { if fit.len() < 3 {
self.command_line self.command_line
.push_error("SPLINEFIT: need at least 3 points."); .push_error(crate::t!("SPLINEFIT: need at least 3 points.").as_ref());
return Task::none(); return Task::none();
} }
let n = fit.len(); let n = fit.len();
@ -756,7 +756,7 @@ impl super::OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
self.command_line self.command_line
.push_output(&format!("SPLINEFIT: fit a spline through {n} points.")); .push_output(crate::tf!("SPLINEFIT: fit a spline through {n} points.").as_ref());
Task::none() Task::none()
} }
@ -776,7 +776,7 @@ impl super::OpenCADStudio {
.collect(); .collect();
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_error("FLATSHOT: select a solid created this session."); .push_error(crate::t!("FLATSHOT: select a solid created this session.").as_ref());
return Task::none(); return Task::none();
} }
self.push_undo_snapshot(i, "FLATSHOT"); self.push_undo_snapshot(i, "FLATSHOT");
@ -797,7 +797,7 @@ impl super::OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
self.command_line self.command_line
.push_output(&format!("FLATSHOT: created {n} projected edge(s) at Z=0.")); .push_output(crate::tf!("FLATSHOT: created {n} projected edge(s) at Z=0.").as_ref());
Task::none() Task::none()
} }
@ -816,7 +816,7 @@ impl super::OpenCADStudio {
.collect(); .collect();
if handles.is_empty() { if handles.is_empty() {
self.command_line self.command_line
.push_error("CONVTOSURFACE: select a solid created this session."); .push_error(crate::t!("CONVTOSURFACE: select a solid created this session.").as_ref());
return Task::none(); return Task::none();
} }
let mut surfaces: Vec<Surface> = Vec::new(); let mut surfaces: Vec<Surface> = Vec::new();
@ -848,7 +848,7 @@ impl super::OpenCADStudio {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_properties(); self.refresh_properties();
self.command_line self.command_line
.push_output(&format!("CONVTOSURFACE: converted {n} solid(s) to surface(s).")); .push_output(crate::tf!("CONVTOSURFACE: converted {n} solid(s) to surface(s).").as_ref());
Task::none() Task::none()
} }
} }

View file

@ -595,7 +595,7 @@ mod tests {
} }
impl ocs_plugin_api::host::InteractiveCommand for PlacePoint { impl ocs_plugin_api::host::InteractiveCommand for PlacePoint {
fn prompt(&self) -> String { fn prompt(&self) -> String {
"Pick a point".into() crate::t!("Pick a point").into_owned()
} }
fn on_point(&mut self, pt: [f64; 3]) -> ocs_plugin_api::host::CommandStep { fn on_point(&mut self, pt: [f64; 3]) -> ocs_plugin_api::host::CommandStep {
use ocs_plugin_api::host::CommandStep; use ocs_plugin_api::host::CommandStep;
@ -632,7 +632,7 @@ mod tests {
struct PickThenMark; struct PickThenMark;
impl ocs_plugin_api::host::InteractiveCommand for PickThenMark { impl ocs_plugin_api::host::InteractiveCommand for PickThenMark {
fn prompt(&self) -> String { fn prompt(&self) -> String {
"Pick an object".into() crate::t!("Pick an object").into_owned()
} }
fn on_point(&mut self, _pt: [f64; 3]) -> ocs_plugin_api::host::CommandStep { fn on_point(&mut self, _pt: [f64; 3]) -> ocs_plugin_api::host::CommandStep {
ocs_plugin_api::host::CommandStep::Cancel ocs_plugin_api::host::CommandStep::Cancel

View file

@ -4,6 +4,7 @@ use crate::io::linetypes;
use crate::scene::view::dispatch; use crate::scene::view::dispatch;
use crate::ui; use crate::ui;
use acadrust::{EntityType, Handle}; use acadrust::{EntityType, Handle};
use crate::t;
/// Above this many selected objects the Properties panel skips per-entity /// Above this many selected objects the Properties panel skips per-entity
/// property aggregation (which is O(n) per row, plus an O(n²) group filter) and /// property aggregation (which is O(n) per row, plus an O(n²) group filter) and
@ -259,30 +260,30 @@ impl OpenCADStudio {
}; };
let sections = vec![ let sections = vec![
PropSection { PropSection {
title: "General".to_string(), title: t!("General").into_owned(),
props: vec![ props: vec![
read_only("AC Version", doc.version.as_str().to_string()), read_only(t!("AC Version").as_ref(), doc.version.as_str().to_string()),
Property { Property {
label: "Color".to_string(), label: t!("Color").into_owned(),
field: "color", field: "color",
value: PropValue::ColorChoice(header.current_entity_color), value: PropValue::ColorChoice(header.current_entity_color),
}, },
Property { Property {
label: "Layer".to_string(), label: t!("Layer").into_owned(),
field: "layer", field: "layer",
value: PropValue::LayerChoice(current_layer), value: PropValue::LayerChoice(current_layer),
}, },
Property { Property {
label: "Linetype".to_string(), label: t!("Linetype").into_owned(),
field: "linetype", field: "linetype",
value: PropValue::LinetypeChoice(current_linetype), value: PropValue::LinetypeChoice(current_linetype),
}, },
read_only( read_only(
"Linetype scale", t!("Linetype scale").as_ref(),
format_length(header.current_entity_linetype_scale), format_length(header.current_entity_linetype_scale),
), ),
Property { Property {
label: "Lineweight".to_string(), label: t!("Lineweight").into_owned(),
field: "line_weight", field: "line_weight",
value: PropValue::LwChoice( value: PropValue::LwChoice(
acadrust::types::LineWeight::from_value( acadrust::types::LineWeight::from_value(
@ -290,21 +291,21 @@ impl OpenCADStudio {
), ),
), ),
}, },
read_only("Transparency", "ByLayer".to_string()), read_only(t!("Transparency").as_ref(), "ByLayer".to_string()),
read_only("Thickness", format_length(header.thickness)), read_only(t!("Thickness").as_ref(), format_length(header.thickness)),
], ],
}, },
PropSection { PropSection {
title: "3D Visualization".to_string(), title: t!("3D Visualization").into_owned(),
props: vec![read_only("Material", material)], props: vec![read_only(t!("Material").as_ref(), material)],
}, },
PropSection { PropSection {
title: "Plot style".to_string(), title: t!("Plot style").into_owned(),
props: vec![ props: vec![
read_only("Plot style", plot_style.to_string()), read_only(t!("Plot style").as_ref(), plot_style.to_string()),
read_only("Plot style table", plot_table.clone()), read_only(t!("Plot style table").as_ref(), plot_table.clone()),
read_only( read_only(
"Plot table attached to", t!("Plot table attached to").as_ref(),
if plot_table == "None" { if plot_table == "None" {
"None".to_string() "None".to_string()
} else { } else {
@ -312,7 +313,7 @@ impl OpenCADStudio {
}, },
), ),
read_only( read_only(
"Plot table type", t!("Plot table type").as_ref(),
if header.plotstyle_mode { if header.plotstyle_mode {
"Named plot styles".to_string() "Named plot styles".to_string()
} else { } else {
@ -322,39 +323,39 @@ impl OpenCADStudio {
], ],
}, },
PropSection { PropSection {
title: "View".to_string(), title: t!("View").into_owned(),
props: vec![ props: vec![
read_only("Center X", format_length(camera.target.x)), read_only(t!("Center X").as_ref(), format_length(camera.target.x)),
read_only("Center Y", format_length(camera.target.y)), read_only(t!("Center Y").as_ref(), format_length(camera.target.y)),
read_only("Center Z", format_length(camera.target.z)), read_only(t!("Center Z").as_ref(), format_length(camera.target.z)),
read_only("Height", format_length(view_height)), read_only(t!("Height").as_ref(), format_length(view_height)),
read_only("Width", format_length(view_width)), read_only(t!("Width").as_ref(), format_length(view_width)),
], ],
}, },
PropSection { PropSection {
title: "Misc".to_string(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
read_only("Annotation scale", annotation_scale), read_only(t!("Annotation scale").as_ref(), annotation_scale),
read_only( read_only(
"UCS icon On", t!("UCS icon On").as_ref(),
if self.show_ucs_icon { "Yes" } else { "No" }.to_string(), if self.show_ucs_icon { "Yes" } else { "No" }.to_string(),
), ),
read_only( read_only(
"UCS icon at origin", t!("UCS icon at origin").as_ref(),
if self.ucs_icon_at_origin { "Yes" } else { "No" } if self.ucs_icon_at_origin { "Yes" } else { "No" }
.to_string(), .to_string(),
), ),
read_only( read_only(
"UCS per viewport", t!("UCS per viewport").as_ref(),
if ucs_per_viewport { "Yes" } else { "No" }.to_string(), if ucs_per_viewport { "Yes" } else { "No" }.to_string(),
), ),
read_only("UCS Name", ucs_name), read_only(t!("UCS Name").as_ref(), ucs_name),
read_only("Visual Style", tab.visual_style.clone()), read_only(t!("Visual Style").as_ref(), tab.visual_style.clone()),
], ],
}, },
]; ];
ui::PropertiesPanel { ui::PropertiesPanel {
title: "No selection".to_string(), title: t!("No selection").into_owned(),
sections, sections,
layer_combo: iced::widget::combo_box::State::new(layer_names.clone()), layer_combo: iced::widget::combo_box::State::new(layer_names.clone()),
linetype_combo: iced::widget::combo_box::State::new( linetype_combo: iced::widget::combo_box::State::new(
@ -435,61 +436,61 @@ impl OpenCADStudio {
{ {
use crate::entities::common::ro_prop; use crate::entities::common::ro_prop;
sections.push(crate::scene::model::object::PropSection { sections.push(crate::scene::model::object::PropSection {
title: "Material Details".to_string(), title: t!("Material Details").into_owned(),
props: vec![ props: vec![
ro_prop("Name", "mat_name", material.name.clone()), ro_prop(t!("Name").as_ref(), "mat_name", material.name.clone()),
ro_prop( ro_prop(
"Description", t!("Description").as_ref(),
"mat_description", "mat_description",
material.description.clone(), material.description.clone(),
), ),
ro_prop( ro_prop(
"Ambient", t!("Ambient").as_ref(),
"mat_ambient", "mat_ambient",
material_color_text(&material.ambient_color), material_color_text(&material.ambient_color),
), ),
ro_prop( ro_prop(
"Diffuse", t!("Diffuse").as_ref(),
"mat_diffuse", "mat_diffuse",
material_color_text(&material.diffuse_color), material_color_text(&material.diffuse_color),
), ),
ro_prop( ro_prop(
"Specular", t!("Specular").as_ref(),
"mat_specular", "mat_specular",
material_color_text(&material.specular_color), material_color_text(&material.specular_color),
), ),
ro_prop( ro_prop(
"Gloss", t!("Gloss").as_ref(),
"mat_gloss", "mat_gloss",
format!("{:.3}", material.specular_gloss_factor), format!("{:.3}", material.specular_gloss_factor),
), ),
ro_prop( ro_prop(
"Opacity", t!("Opacity").as_ref(),
"mat_opacity", "mat_opacity",
format!("{:.3}", material.opacity_percent), format!("{:.3}", material.opacity_percent),
), ),
ro_prop( ro_prop(
"Reflectivity", t!("Reflectivity").as_ref(),
"mat_reflectivity", "mat_reflectivity",
format!("{:.3}", material.reflectivity), format!("{:.3}", material.reflectivity),
), ),
ro_prop( ro_prop(
"Translucence", t!("Translucence").as_ref(),
"mat_translucence", "mat_translucence",
format!("{:.3}", material.translucence), format!("{:.3}", material.translucence),
), ),
ro_prop( ro_prop(
"Refraction", t!("Refraction").as_ref(),
"mat_refraction", "mat_refraction",
format!("{:.3}", material.refraction_index), format!("{:.3}", material.refraction_index),
), ),
ro_prop( ro_prop(
"Self Illumination", t!("Self Illumination").as_ref(),
"mat_self_illumination", "mat_self_illumination",
format!("{:.3}", material.self_illumination), format!("{:.3}", material.self_illumination),
), ),
ro_prop( ro_prop(
"Luminance", t!("Luminance").as_ref(),
"mat_luminance", "mat_luminance",
format!( format!(
"{:.3} (mode {})", "{:.3} (mode {})",
@ -497,42 +498,42 @@ impl OpenCADStudio {
), ),
), ),
ro_prop( ro_prop(
"Diffuse Map", t!("Diffuse Map").as_ref(),
"mat_diffuse_map", "mat_diffuse_map",
material_map_text(&material.diffuse_map), material_map_text(&material.diffuse_map),
), ),
ro_prop( ro_prop(
"Specular Map", t!("Specular Map").as_ref(),
"mat_specular_map", "mat_specular_map",
material_map_text(&material.specular_map), material_map_text(&material.specular_map),
), ),
ro_prop( ro_prop(
"Reflection Map", t!("Reflection Map").as_ref(),
"mat_reflection_map", "mat_reflection_map",
material_map_text(&material.reflection_map), material_map_text(&material.reflection_map),
), ),
ro_prop( ro_prop(
"Opacity Map", t!("Opacity Map").as_ref(),
"mat_opacity_map", "mat_opacity_map",
material_map_text(&material.opacity_map), material_map_text(&material.opacity_map),
), ),
ro_prop( ro_prop(
"Bump Map", t!("Bump Map").as_ref(),
"mat_bump_map", "mat_bump_map",
material_map_text(&material.bump_map), material_map_text(&material.bump_map),
), ),
ro_prop( ro_prop(
"Refraction Map", t!("Refraction Map").as_ref(),
"mat_refraction_map", "mat_refraction_map",
material_map_text(&material.refraction_map), material_map_text(&material.refraction_map),
), ),
ro_prop( ro_prop(
"Normal Map", t!("Normal Map").as_ref(),
"mat_normal_map", "mat_normal_map",
material_map_text(&material.normal_map), material_map_text(&material.normal_map),
), ),
ro_prop( ro_prop(
"Render Flags", t!("Render Flags").as_ref(),
"mat_render_flags", "mat_render_flags",
format!( format!(
"illumination {}; channels {}; mode {}; two-sided {}", "illumination {}; channels {}; mode {}; two-sided {}",
@ -543,7 +544,7 @@ impl OpenCADStudio {
), ),
), ),
ro_prop( ro_prop(
"Advanced", t!("Advanced").as_ref(),
"mat_advanced", "mat_advanced",
format!( format!(
"normal {:.3}; bump {:.3}; reflect {:.3}; transmit {:.3}", "normal {:.3}; bump {:.3}; reflect {:.3}; transmit {:.3}",
@ -578,16 +579,16 @@ impl OpenCADStudio {
} }
use crate::entities::common::ro_prop; use crate::entities::common::ro_prop;
sections.push(crate::scene::model::object::PropSection { sections.push(crate::scene::model::object::PropSection {
title: "Color Book".to_string(), title: t!("Color Book").into_owned(),
props: vec![ props: vec![
ro_prop("Book", "book_color_book", book.book_name.clone()), ro_prop(t!("Book").as_ref(), "book_color_book", book.book_name.clone()),
ro_prop( ro_prop(
"Color Name", t!("Color Name").as_ref(),
"book_color_name", "book_color_name",
book.color_name.clone(), book.color_name.clone(),
), ),
ro_prop( ro_prop(
"Color", t!("Color").as_ref(),
"book_color_value", "book_color_value",
format!("{:?}", book.color), format!("{:?}", book.color),
), ),
@ -616,25 +617,25 @@ impl OpenCADStudio {
}; };
use crate::entities::common::ro_prop; use crate::entities::common::ro_prop;
sections.push(crate::scene::model::object::PropSection { sections.push(crate::scene::model::object::PropSection {
title: format!("{scope} Visual Style"), title: t!("%{scope} Visual Style", scope = scope).into_owned(),
props: vec![ props: vec![
ro_prop( ro_prop(
"Handle", t!("Handle").as_ref(),
"vs_handle", "vs_handle",
format!("{:X}", handle.value()), format!("{:X}", handle.value()),
), ),
ro_prop( ro_prop(
"Description", t!("Description").as_ref(),
"vs_description", "vs_description",
style.description.clone(), style.description.clone(),
), ),
ro_prop( ro_prop(
"Type", t!("Type").as_ref(),
"vs_type", "vs_type",
style.style_type.to_string(), style.style_type.to_string(),
), ),
ro_prop( ro_prop(
"Face", t!("Face").as_ref(),
"vs_face", "vs_face",
format!( format!(
"lighting {}; quality {}; color {}; modifiers {}", "lighting {}; quality {}; color {}; modifiers {}",
@ -645,7 +646,7 @@ impl OpenCADStudio {
), ),
), ),
ro_prop( ro_prop(
"Edges", t!("Edges").as_ref(),
"vs_edges", "vs_edges",
format!( format!(
"model {}; style {}", "model {}; style {}",
@ -653,17 +654,17 @@ impl OpenCADStudio {
), ),
), ),
ro_prop( ro_prop(
"Extended Lighting", t!("Extended Lighting").as_ref(),
"vs_extended_lighting", "vs_extended_lighting",
style.extended_lighting_model.to_string(), style.extended_lighting_model.to_string(),
), ),
ro_prop( ro_prop(
"Internal", t!("Internal").as_ref(),
"vs_internal", "vs_internal",
style.internal_use_only.to_string(), style.internal_use_only.to_string(),
), ),
ro_prop( ro_prop(
"Property Bag", t!("Property Bag").as_ref(),
"vs_properties", "vs_properties",
visual_style_properties_text(style), visual_style_properties_text(style),
), ),
@ -692,22 +693,22 @@ impl OpenCADStudio {
let metrics = mesh.metrics; let metrics = mesh.metrics;
let mut props = vec![ let mut props = vec![
ro_prop( ro_prop(
"Vertices", t!("Vertices").as_ref(),
"mesh_vertices", "mesh_vertices",
metrics.vertices.to_string(), metrics.vertices.to_string(),
), ),
ro_prop( ro_prop(
"Triangles", t!("Triangles").as_ref(),
"mesh_triangles", "mesh_triangles",
metrics.triangles.to_string(), metrics.triangles.to_string(),
), ),
ro_prop( ro_prop(
"Surface Area", t!("Surface Area").as_ref(),
"mesh_surface_area", "mesh_surface_area",
format!("{:.6}", metrics.surface_area), format!("{:.6}", metrics.surface_area),
), ),
ro_prop( ro_prop(
"Centroid", t!("Centroid").as_ref(),
"mesh_centroid", "mesh_centroid",
format!( format!(
"{:.6}, {:.6}, {:.6}", "{:.6}, {:.6}, {:.6}",
@ -717,7 +718,7 @@ impl OpenCADStudio {
), ),
), ),
ro_prop( ro_prop(
"Tessellation", t!("Tessellation").as_ref(),
"mesh_complete", "mesh_complete",
if mesh.complete { "Complete" } else { "Partial" }, if mesh.complete { "Complete" } else { "Partial" },
), ),
@ -733,14 +734,14 @@ impl OpenCADStudio {
props.insert( props.insert(
3, 3,
ro_prop( ro_prop(
"Volume", t!("Volume").as_ref(),
"mesh_volume", "mesh_volume",
format!("{:.6}", metrics.volume), format!("{:.6}", metrics.volume),
), ),
); );
} }
sections.push(crate::scene::model::object::PropSection { sections.push(crate::scene::model::object::PropSection {
title: "Mass Properties".to_string(), title: t!("Mass Properties").into_owned(),
props, props,
}); });
} }
@ -770,7 +771,7 @@ impl OpenCADStudio {
continue; continue;
}; };
let toggle = crate::scene::model::object::Property { let toggle = crate::scene::model::object::Property {
label: "Uniform scale".into(), label: t!("Uniform scale").into_owned(),
field: "ins_uniform", field: "ins_uniform",
value: crate::scene::model::object::PropValue::BoolToggle { value: crate::scene::model::object::PropValue::BoolToggle {
field: "ins_uniform", field: "ins_uniform",
@ -787,7 +788,7 @@ impl OpenCADStudio {
.position(|p| p.field == "x_scale") .position(|p| p.field == "x_scale")
.unwrap_or(xi.min(section.props.len())); .unwrap_or(xi.min(section.props.len()));
section.props[xi] = crate::entities::common::edit_prop( section.props[xi] = crate::entities::common::edit_prop(
"Scale", t!("Scale").as_ref(),
"u_scale", "u_scale",
ins.x_scale(), ins.x_scale(),
); );
@ -975,7 +976,7 @@ impl OpenCADStudio {
if let Some(geom) = sections.last_mut() { if let Some(geom) = sections.last_mut() {
geom.props.push(crate::scene::model::object::Property { geom.props.push(crate::scene::model::object::Property {
label: "Frozen Layers".to_string(), label: t!("Frozen Layers").into_owned(),
field: "frozen_layers", field: "frozen_layers",
value: crate::scene::model::object::PropValue::EditText( value: crate::scene::model::object::PropValue::EditText(
frozen_names.join(", "), frozen_names.join(", "),
@ -983,7 +984,7 @@ impl OpenCADStudio {
}); });
if !ucs_names.is_empty() { if !ucs_names.is_empty() {
geom.props.push(crate::scene::model::object::Property { geom.props.push(crate::scene::model::object::Property {
label: "UCS Name".to_string(), label: t!("UCS Name").into_owned(),
field: "vp_ucs_name", field: "vp_ucs_name",
value: crate::scene::model::object::PropValue::Choice { value: crate::scene::model::object::PropValue::Choice {
selected: current_ucs, selected: current_ucs,
@ -993,7 +994,7 @@ impl OpenCADStudio {
} }
if !view_names.is_empty() { if !view_names.is_empty() {
geom.props.push(crate::scene::model::object::Property { geom.props.push(crate::scene::model::object::Property {
label: "Named View".to_string(), label: t!("Named View").into_owned(),
field: "vp_named_view", field: "vp_named_view",
value: crate::scene::model::object::PropValue::Choice { value: crate::scene::model::object::PropValue::Choice {
selected: String::new(), selected: String::new(),
@ -1385,7 +1386,7 @@ impl OpenCADStudio {
&mut sections, &mut sections,
anchor, anchor,
crate::entities::common::ro_prop( crate::entities::common::ro_prop(
"Annotative", t!("Annotative").as_ref(),
"annotative", "annotative",
"No", "No",
), ),
@ -1430,7 +1431,7 @@ impl OpenCADStudio {
&mut sections, &mut sections,
anno_field, anno_field,
crate::entities::common::ro_prop( crate::entities::common::ro_prop(
"Annotative scale", t!("Annotative scale").as_ref(),
"annotative_scale", "annotative_scale",
doc.header.current_annotation_scale.clone(), doc.header.current_annotation_scale.clone(),
), ),
@ -1443,7 +1444,7 @@ impl OpenCADStudio {
let label = group_names.join(", "); let label = group_names.join(", ");
if let Some(general) = sections.first_mut() { if let Some(general) = sections.first_mut() {
general.props.push(crate::scene::model::object::Property { general.props.push(crate::scene::model::object::Property {
label: "Group".to_string(), label: t!("Group").into_owned(),
field: "group", field: "group",
value: crate::scene::model::object::PropValue::ReadOnly(label), value: crate::scene::model::object::PropValue::ReadOnly(label),
}); });
@ -1460,7 +1461,7 @@ impl OpenCADStudio {
.map(|br| br.flags.is_xref || br.flags.is_xref_overlay) .map(|br| br.flags.is_xref || br.flags.is_xref_overlay)
.unwrap_or(false); .unwrap_or(false);
if is_xref { if is_xref {
"External Reference".to_string() t!("External Reference").into_owned()
} else { } else {
entity_type_label(entity) entity_type_label(entity)
} }
@ -1474,7 +1475,13 @@ impl OpenCADStudio {
.filter_map(|prop| match &prop.value { .filter_map(|prop| match &prop.value {
crate::scene::model::object::PropValue::Choice { options, .. } => Some(( crate::scene::model::object::PropValue::Choice { options, .. } => Some((
prop.field.to_string(), prop.field.to_string(),
iced::widget::combo_box::State::new(options.clone()), iced::widget::combo_box::State::new(
options
.iter()
.cloned()
.map(ui::properties::LocalizedChoice::new)
.collect(),
),
)), )),
_ => None, _ => None,
}) })
@ -1495,7 +1502,7 @@ impl OpenCADStudio {
// for seconds at tens of thousands of objects. Above the cap show a // for seconds at tens of thousands of objects. Above the cap show a
// count-only panel; bulk edits still go through the ribbon. // count-only panel; bulk edits still go through the ribbon.
n if n > MAX_PROP_AGGREGATE => ui::PropertiesPanel { n if n > MAX_PROP_AGGREGATE => ui::PropertiesPanel {
title: format!("{} objects selected", n), title: t!("%{count} objects selected", count = n).into_owned(),
layer_combo: iced::widget::combo_box::State::new(layer_names.clone()), layer_combo: iced::widget::combo_box::State::new(layer_names.clone()),
linetype_combo: iced::widget::combo_box::State::new(linetype_items.clone()), linetype_combo: iced::widget::combo_box::State::new(linetype_items.clone()),
lineweight_combo: iced::widget::combo_box::State::new( lineweight_combo: iced::widget::combo_box::State::new(
@ -1529,13 +1536,19 @@ impl OpenCADStudio {
.filter_map(|prop| match &prop.value { .filter_map(|prop| match &prop.value {
crate::scene::model::object::PropValue::Choice { options, .. } => Some(( crate::scene::model::object::PropValue::Choice { options, .. } => Some((
prop.field.to_string(), prop.field.to_string(),
iced::widget::combo_box::State::new(options.clone()), iced::widget::combo_box::State::new(
options
.iter()
.cloned()
.map(ui::properties::LocalizedChoice::new)
.collect(),
),
)), )),
_ => None, _ => None,
}) })
.collect(), .collect(),
sections, sections,
title: format!("{} objects selected", selected.len()), title: t!("%{count} objects selected", count = selected.len()).into_owned(),
selection_group_combo: iced::widget::combo_box::State::new(groups.clone()), selection_group_combo: iced::widget::combo_box::State::new(groups.clone()),
selection_groups: groups, selection_groups: groups,
selected_group: active_group, selected_group: active_group,
@ -1998,7 +2011,7 @@ impl OpenCADStudio {
} }
Err(e) => { Err(e) => {
self.command_line self.command_line
.push_error(&format!("Viewport could not be added: {e}")); .push_error(crate::tf!("Viewport could not be added: {e}").as_ref());
None None
} }
} }
@ -2037,7 +2050,7 @@ pub(super) fn build_selection_groups(
selected: &[(Handle, &EntityType)], selected: &[(Handle, &EntityType)],
) -> Vec<ui::properties::SelectionGroup> { ) -> Vec<ui::properties::SelectionGroup> {
let mut groups = vec![ui::properties::SelectionGroup { let mut groups = vec![ui::properties::SelectionGroup {
label: format!("All({})", selected.len()), label: format!("{} ({})", t!("All").into_owned(), selected.len()),
handles: selected.iter().map(|(handle, _)| *handle).collect(), handles: selected.iter().map(|(handle, _)| *handle).collect(),
}]; }];
@ -2052,7 +2065,7 @@ pub(super) fn build_selection_groups(
for (kind, handles) in by_type { for (kind, handles) in by_type {
groups.push(ui::properties::SelectionGroup { groups.push(ui::properties::SelectionGroup {
label: format!("{}({})", title_case_word(&kind), handles.len()), label: format!("{}({})", t!(title_case_word(&kind)), handles.len()),
handles, handles,
}); });
} }

View file

@ -593,7 +593,7 @@ impl OpenCADStudio {
self.load_style_bufs(kind); self.load_style_bufs(kind);
self.after_style_change(kind); self.after_style_change(kind);
self.command_line self.command_line
.push_output(&format!("Style '{name}' created.")); .push_output(crate::tf!("Style '{name}' created.").as_ref());
} }
pub(super) fn style_copy(&mut self, kind: StyleKind) { pub(super) fn style_copy(&mut self, kind: StyleKind) {
@ -608,19 +608,19 @@ impl OpenCADStudio {
self.load_style_bufs(kind); self.load_style_bufs(kind);
self.after_style_change(kind); self.after_style_change(kind);
self.command_line self.command_line
.push_output(&format!("Style '{name}' created.")); .push_output(crate::tf!("Style '{name}' created.").as_ref());
} }
pub(super) fn style_delete(&mut self, kind: StyleKind) { pub(super) fn style_delete(&mut self, kind: StyleKind) {
let name = self.style_selected(kind); let name = self.style_selected(kind);
if name.eq_ignore_ascii_case("Standard") { if name.eq_ignore_ascii_case("Standard") {
self.command_line self.command_line
.push_error("Cannot delete the Standard style."); .push_error(crate::t!("Cannot delete the Standard style.").as_ref());
return; return;
} }
if self.style_in_use(kind, &name) { if self.style_in_use(kind, &name) {
self.command_line self.command_line
.push_error("Cannot delete a style that is current or in use."); .push_error(crate::t!("Cannot delete a style that is current or in use.").as_ref());
return; return;
} }
if !self.remove_style_storage(kind, &name) { if !self.remove_style_storage(kind, &name) {
@ -635,7 +635,7 @@ impl OpenCADStudio {
self.load_style_bufs(kind); self.load_style_bufs(kind);
self.after_style_change(kind); self.after_style_change(kind);
self.command_line self.command_line
.push_output(&format!("Style '{name}' deleted.")); .push_output(crate::tf!("Style '{name}' deleted.").as_ref());
} }
/// Begin inline rename of the double-clicked style. /// Begin inline rename of the double-clicked style.
@ -659,12 +659,12 @@ impl OpenCADStudio {
} }
if old.eq_ignore_ascii_case("Standard") { if old.eq_ignore_ascii_case("Standard") {
self.command_line self.command_line
.push_error("Cannot rename the Standard style."); .push_error(crate::t!("Cannot rename the Standard style.").as_ref());
return; return;
} }
if self.style_exists(kind, &new) { if self.style_exists(kind, &new) {
self.command_line self.command_line
.push_error(&format!("Style '{new}' already exists.")); .push_error(crate::tf!("Style '{new}' already exists.").as_ref());
return; return;
} }
self.rename_style_storage(kind, &old, &new); self.rename_style_storage(kind, &old, &new);
@ -674,7 +674,7 @@ impl OpenCADStudio {
self.load_style_bufs(kind); self.load_style_bufs(kind);
self.after_style_change(kind); self.after_style_change(kind);
self.command_line self.command_line
.push_output(&format!("Renamed '{old}' → '{new}'.")); .push_output(crate::tf!("Renamed '{old}' → '{new}'.").as_ref());
} }
pub(super) fn style_rename_cancel(&mut self) { pub(super) fn style_rename_cancel(&mut self) {

View file

@ -224,10 +224,10 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
let raw = crate::app::expr_eval::eval_to_string(self.command_line.input.trim()); let raw = crate::app::expr_eval::eval_to_string(self.command_line.input.trim());
self.command_line.input.clear(); self.command_line.input.clear();
let Ok(v) = raw.parse::<f64>() else { let Ok(v) = raw.parse::<f64>() else {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"{}: expected a number, got \"{raw}\"", "{}: expected a number, got \"{raw}\"",
pending.label pending.label
)); ).as_ref());
return Task::none(); return Task::none();
}; };
let i = self.active_tab; let i = self.active_tab;
@ -443,9 +443,9 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
return self.apply_cmd_result(result); return self.apply_cmd_result(result);
} }
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"Expected coordinates (x,y) or a number, got: \"{text}\"" "Expected coordinates (x,y) or a number, got: \"{text}\""
)); ).as_ref());
return self.focus_cmd_input(); return self.focus_cmd_input();
} }
if let Some(cmd) = self.command_line.submit() { if let Some(cmd) = self.command_line.submit() {
@ -519,7 +519,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
} }
// Esc cancels a pending system-variable value prompt. // Esc cancels a pending system-variable value prompt.
if self.pending_setvar.take().is_some() { if self.pending_setvar.take().is_some() {
self.command_line.push_info("*Cancel*"); self.command_line.push_info(crate::t!("*Cancel*").as_ref());
return Task::none(); return Task::none();
} }
// UCS icon: Esc ends any grip drag and clears the selection // UCS icon: Esc ends any grip drag and clears the selection
@ -548,7 +548,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
sel.middle_down = false; sel.middle_down = false;
sel.middle_last_pos = None; sel.middle_last_pos = None;
} }
self.command_line.push_output("PAN ended."); self.command_line.push_output(crate::t!("PAN ended.").as_ref());
return Task::none(); return Task::none();
} }
// Grip popup intercepts Escape — dismisses the menu // Grip popup intercepts Escape — dismisses the menu
@ -738,7 +738,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
names.retain(|n| n != "0" && *n != current); names.retain(|n| n != "0" && *n != current);
if names.len() < before { if names.len() < before {
self.command_line self.command_line
.push_info("Layer \"0\" and the current layer can't be deleted — skipped."); .push_info(crate::t!("Layer \"0\" and the current layer can't be deleted — skipped.").as_ref());
} }
if names.is_empty() { if names.is_empty() {
return Task::none(); return Task::none();
@ -971,7 +971,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
action: item.action, action: item.action,
label, label,
}); });
self.command_line.push_info(&format!("{label}:")); self.command_line.push_info(crate::tf!("{label}:").as_ref());
return self.focus_cmd_input(); return self.focus_cmd_input();
} }
// Break at vertex replaces the entity with the split pieces — // Break at vertex replaces the entity with the split pieces —
@ -993,11 +993,11 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_selected_grips(); self.refresh_selected_grips();
self.refresh_properties(); self.refresh_properties();
self.command_line.push_output("Polyline broken at vertex."); self.command_line.push_output(crate::t!("Polyline broken at vertex.").as_ref());
} }
None => self None => self
.command_line .command_line
.push_error("Cannot break at this vertex."), .push_error(crate::t!("Cannot break at this vertex.").as_ref()),
} }
return Task::none(); return Task::none();
} }
@ -1042,7 +1042,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
self.tabs[i].dirty = dirty_before; self.tabs[i].dirty = dirty_before;
self.refresh_selected_grips(); self.refresh_selected_grips();
self.refresh_properties(); self.refresh_properties();
self.command_line.push_error("Cannot add a vertex here."); self.command_line.push_error(crate::t!("Cannot add a vertex here.").as_ref());
return Task::none(); return Task::none();
} }
self.tabs[i] self.tabs[i]
@ -1069,7 +1069,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
grip_world, grip_world,
)); ));
self.command_line self.command_line
.push_info("Specify new vertex location:"); .push_info(crate::t!("Specify new vertex location:").as_ref());
} else { } else {
if let Some(entity) = if let Some(entity) =
self.tabs[i].scene.document.get_entity_mut(popup.handle) self.tabs[i].scene.document.get_entity_mut(popup.handle)
@ -1083,7 +1083,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
self.refresh_selected_grips(); self.refresh_selected_grips();
self.refresh_properties(); self.refresh_properties();
self.command_line self.command_line
.push_error("Cannot place the new vertex."); .push_error(crate::t!("Cannot place the new vertex.").as_ref());
} }
return Task::none(); return Task::none();
} }
@ -2084,9 +2084,9 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
// The block's layer may have been locked while the editor was open — // The block's layer may have been locked while the editor was open —
// refuse to write attributes to a locked-layer block. // refuse to write attributes to a locked-layer block.
if let Some(layer) = self.tabs[i].scene.locked_layer_name(handle) { if let Some(layer) = self.tabs[i].scene.locked_layer_name(handle) {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"Object is on locked layer \"{layer}\" — unlock the layer to edit its attributes." "Object is on locked layer \"{layer}\" — unlock the layer to edit its attributes."
)); ).as_ref());
return Task::none(); return Task::none();
} }
// Snapshot the working copy so the document can be mutated while the // Snapshot the working copy so the document can be mutated while the

View file

@ -116,7 +116,7 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
if self.tabs[self.active_tab].is_start && !matches!(event, ModuleEvent::Command(_)) { if self.tabs[self.active_tab].is_start && !matches!(event, ModuleEvent::Command(_)) {
self.ribbon.close_dropdown(); self.ribbon.close_dropdown();
self.command_line self.command_line
.push_info("No drawing open — use New or Open first."); .push_info(crate::t!("No drawing open — use New or Open first.").as_ref());
return Task::none(); return Task::none();
} }
// Dismiss any open dropdown / collapsed-panel flyout on tool use, // Dismiss any open dropdown / collapsed-panel flyout on tool use,
@ -141,13 +141,13 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
} }
ModuleEvent::OpenFileDialog => { ModuleEvent::OpenFileDialog => {
self.command_line self.command_line
.push_info("Open DWG/DXF: not yet implemented."); .push_info(crate::t!("Open DWG/DXF: not yet implemented.").as_ref());
} }
ModuleEvent::ClearModels => { ModuleEvent::ClearModels => {
let i = self.active_tab; let i = self.active_tab;
self.tabs[i].scene.clear(); self.tabs[i].scene.clear();
self.tabs[i].properties = PropertiesPanel::empty(); self.tabs[i].properties = PropertiesPanel::empty();
self.command_line.push_output("Scene cleared."); self.command_line.push_output(crate::t!("Scene cleared.").as_ref());
} }
ModuleEvent::SetWireframe(w) => { ModuleEvent::SetWireframe(w) => {
let i = self.active_tab; let i = self.active_tab;
@ -265,7 +265,7 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
if self.active_save_jobs.contains_key(&self.tabs[idx].id) { if self.active_save_jobs.contains_key(&self.tabs[idx].id) {
self.command_line self.command_line
.push_info("Save already running for this drawing."); .push_info(crate::t!("Save already running for this drawing.").as_ref());
return Task::none(); return Task::none();
} }

View file

@ -659,7 +659,10 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
.is_some_and(|(loaded, current)| loaded != current) .is_some_and(|(loaded, current)| loaded != current)
{ {
self.command_line.push_error_once( self.command_line.push_error_once(
"Drawing changed on disk while it was opening; Save will require conflict resolution.", crate::t!(
"Drawing changed on disk while it was opening; Save will require conflict resolution."
)
.as_ref(),
); );
} }
self.tabs[i].disk_fingerprint = self.tabs[i].disk_fingerprint =
@ -667,9 +670,9 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
match crate::io::edit_lock::EditLease::acquire(path) { match crate::io::edit_lock::EditLease::acquire(path) {
Ok(lease) => { Ok(lease) => {
if let Some(warning) = lease.platform_warning() { if let Some(warning) = lease.platform_warning() {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"Edit lease active; {warning}. External-change checks remain active." "Edit lease active; {warning}. External-change checks remain active."
)); ).as_ref());
} }
self.tabs[i].edit_lease = Some(lease); self.tabs[i].edit_lease = Some(lease);
self.tabs[i].edit_lock_conflict = false; self.tabs[i].edit_lock_conflict = false;
@ -677,16 +680,16 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
Err(crate::io::edit_lock::EditLeaseError::Locked(error)) => { Err(crate::io::edit_lock::EditLeaseError::Locked(error)) => {
self.tabs[i].edit_lease = None; self.tabs[i].edit_lease = None;
self.tabs[i].edit_lock_conflict = true; self.tabs[i].edit_lock_conflict = true;
self.command_line.push_error_once(&format!( self.command_line.push_error_once(crate::tf!(
"Opened read-only against other editors: {error}" "Opened read-only against other editors: {error}"
)); ).as_ref());
} }
Err(crate::io::edit_lock::EditLeaseError::Unavailable(error)) => { Err(crate::io::edit_lock::EditLeaseError::Unavailable(error)) => {
self.tabs[i].edit_lease = None; self.tabs[i].edit_lease = None;
self.tabs[i].edit_lock_conflict = false; self.tabs[i].edit_lock_conflict = false;
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"{error}. External-change checks remain active." "{error}. External-change checks remain active."
)); ).as_ref());
} }
} }
} }
@ -718,22 +721,22 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
Some(Ok(warning)) => { Some(Ok(warning)) => {
self.tabs[i].edit_lock_conflict = false; self.tabs[i].edit_lock_conflict = false;
if let Some(warning) = warning { if let Some(warning) = warning {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"{warning}. External-change checks remain active." "{warning}. External-change checks remain active."
)); ).as_ref());
} }
} }
Some(Err(crate::io::edit_lock::EditLeaseError::Locked(error))) => { Some(Err(crate::io::edit_lock::EditLeaseError::Locked(error))) => {
self.tabs[i].edit_lock_conflict = true; self.tabs[i].edit_lock_conflict = true;
self.command_line.push_error_once(&format!( self.command_line.push_error_once(crate::tf!(
"Saved, but the refreshed drawing is locked by another editor: {error}" "Saved, but the refreshed drawing is locked by another editor: {error}"
)); ).as_ref());
} }
Some(Err(crate::io::edit_lock::EditLeaseError::Unavailable(error))) => { Some(Err(crate::io::edit_lock::EditLeaseError::Unavailable(error))) => {
self.tabs[i].edit_lock_conflict = false; self.tabs[i].edit_lock_conflict = false;
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"{error}. External-change checks remain active." "{error}. External-change checks remain active."
)); ).as_ref());
} }
None => self.install_native_edit_guard(i, path, None), None => self.install_native_edit_guard(i, path, None),
} }
@ -765,9 +768,9 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
return Err(crate::io::SaveFailure::file_in_use(error)); return Err(crate::io::SaveFailure::file_in_use(error));
} }
Err(crate::io::edit_lock::EditLeaseError::Unavailable(error)) => { Err(crate::io::edit_lock::EditLeaseError::Unavailable(error)) => {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"{error}. External-change checks remain active." "{error}. External-change checks remain active."
)); ).as_ref());
None None
} }
} }
@ -829,9 +832,9 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
let warning = lease.platform_warning().map(str::to_owned); let warning = lease.platform_warning().map(str::to_owned);
self.tabs[i].edit_lock_conflict = false; self.tabs[i].edit_lock_conflict = false;
if let Some(warning) = warning { if let Some(warning) = warning {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"{warning}. External-change checks remain active." "{warning}. External-change checks remain active."
)); ).as_ref());
} }
return Ok(()); return Ok(());
} }
@ -840,9 +843,9 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
} }
Err(crate::io::edit_lock::EditLeaseError::Unavailable(error)) => { Err(crate::io::edit_lock::EditLeaseError::Unavailable(error)) => {
self.tabs[i].edit_lock_conflict = false; self.tabs[i].edit_lock_conflict = false;
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"{error}. External-change checks remain active." "{error}. External-change checks remain active."
)); ).as_ref());
return Ok(()); return Ok(());
} }
} }
@ -854,18 +857,18 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
self.tabs[i].edit_lease = Some(lease); self.tabs[i].edit_lease = Some(lease);
self.tabs[i].edit_lock_conflict = false; self.tabs[i].edit_lock_conflict = false;
if let Some(warning) = warning { if let Some(warning) = warning {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"{warning}. External-change checks remain active." "{warning}. External-change checks remain active."
)); ).as_ref());
} }
Ok(()) Ok(())
} }
Err(crate::io::edit_lock::EditLeaseError::Locked(error)) => Err(error), Err(crate::io::edit_lock::EditLeaseError::Locked(error)) => Err(error),
Err(crate::io::edit_lock::EditLeaseError::Unavailable(error)) => { Err(crate::io::edit_lock::EditLeaseError::Unavailable(error)) => {
self.tabs[i].edit_lock_conflict = false; self.tabs[i].edit_lock_conflict = false;
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"{error}. External-change checks remain active." "{error}. External-change checks remain active."
)); ).as_ref());
Ok(()) Ok(())
} }
} }
@ -883,34 +886,34 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
let timings = caches.timings; let timings = caches.timings;
let entity_count = doc.entities().count(); let entity_count = doc.entities().count();
self.command_line self.command_line
.push_output(&format!("Opened \"{name}\" — {entity_count} entities")); .push_output(crate::tf!("Opened \"{name}\" — {entity_count} entities").as_ref());
if caches.corrupt_dropped > 0 { if caches.corrupt_dropped > 0 {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"Warning: {} corrupt entities dropped (parser junk — bad normals / counts)", "Warning: {} corrupt entities dropped (parser junk — bad normals / counts)",
caches.corrupt_dropped caches.corrupt_dropped
)); ).as_ref());
} }
if caches.xref_dropped > 0 { if caches.xref_dropped > 0 {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"Warning: {} corrupt xref entities dropped", "Warning: {} corrupt xref entities dropped",
caches.xref_dropped caches.xref_dropped
)); ).as_ref());
} }
for info in &caches.xrefs { for info in &caches.xrefs {
match info.status { match info.status {
crate::io::xref::XrefStatus::Loaded => { crate::io::xref::XrefStatus::Loaded => {
self.command_line self.command_line
.push_output(&format!("XREF Loaded \"{}\"", info.name)); .push_output(crate::tf!("XREF Loaded \"{}\"", info.name).as_ref());
} }
crate::io::xref::XrefStatus::NotFound => { crate::io::xref::XrefStatus::NotFound => {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"XREF Not found: \"{}\" ({})", "XREF Not found: \"{}\" ({})",
info.name, info.path info.name, info.path
)); ).as_ref());
} }
crate::io::xref::XrefStatus::Unloaded => { crate::io::xref::XrefStatus::Unloaded => {
self.command_line self.command_line
.push_info(&format!("XREF Unloaded (skipped): \"{}\"", info.name)); .push_info(crate::tf!("XREF Unloaded (skipped): \"{}\"", info.name).as_ref());
} }
} }
} }
@ -982,10 +985,10 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
let total_ms = open_started let total_ms = open_started
.map(|s| s.elapsed().as_millis() as u32) .map(|s| s.elapsed().as_millis() as u32)
.unwrap_or(0); .unwrap_or(0);
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
" parse {}ms · purge {}ms · caches {}ms · xref {}ms · total {}ms", " parse {}ms · purge {}ms · caches {}ms · xref {}ms · total {}ms",
timings.parse_ms, timings.purge_ms, timings.caches_ms, timings.xref_ms, total_ms timings.parse_ms, timings.purge_ms, timings.caches_ms, timings.xref_ms, total_ms
)); ).as_ref());
// Caches were built on the background thread inside open_path(). // Caches were built on the background thread inside open_path().
self.tabs[i].scene.local_extent_max = caches.local_extent_max; self.tabs[i].scene.local_extent_max = caches.local_extent_max;
@ -1209,7 +1212,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
if self.active_save_jobs.contains_key(&tab_id) { if self.active_save_jobs.contains_key(&tab_id) {
if purpose != crate::app::SavePurpose::Autosave { if purpose != crate::app::SavePurpose::Autosave {
self.command_line self.command_line
.push_info("Save already running for this drawing."); .push_info(crate::t!("Save already running for this drawing.").as_ref());
} }
return Task::none(); return Task::none();
} }
@ -1218,12 +1221,12 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
&& self.tabs[i].edit_lock_conflict && self.tabs[i].edit_lock_conflict
{ {
let error = "Drawing edit lock is held by another editor.".to_string(); let error = "Drawing edit lock is held by another editor.".to_string();
self.command_line.push_error_once(&format!( self.command_line.push_error_once(crate::tf!(
"Unable to save \"{}\": {error}", "Unable to save \"{}\": {error}",
path.file_name() path.file_name()
.map(|name| name.to_string_lossy().into_owned()) .map(|name| name.to_string_lossy().into_owned())
.unwrap_or_else(|| path.display().to_string()) .unwrap_or_else(|| path.display().to_string())
)); ).as_ref());
self.pending_save_failure = Some(crate::app::PendingSaveFailure { self.pending_save_failure = Some(crate::app::PendingSaveFailure {
tab_id, tab_id,
path, path,
@ -1248,12 +1251,12 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
self.pending_save_leases.insert(tab_id, lease); self.pending_save_leases.insert(tab_id, lease);
} }
Err(crate::io::edit_lock::EditLeaseError::Locked(error)) => { Err(crate::io::edit_lock::EditLeaseError::Locked(error)) => {
self.command_line.push_error_once(&format!( self.command_line.push_error_once(crate::tf!(
"Unable to save \"{}\": {error}", "Unable to save \"{}\": {error}",
path.file_name() path.file_name()
.map(|name| name.to_string_lossy().into_owned()) .map(|name| name.to_string_lossy().into_owned())
.unwrap_or_else(|| path.display().to_string()) .unwrap_or_else(|| path.display().to_string())
)); ).as_ref());
self.pending_save_failure = Some(crate::app::PendingSaveFailure { self.pending_save_failure = Some(crate::app::PendingSaveFailure {
tab_id, tab_id,
path, path,
@ -1268,9 +1271,9 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
return Task::none(); return Task::none();
} }
Err(crate::io::edit_lock::EditLeaseError::Unavailable(error)) => { Err(crate::io::edit_lock::EditLeaseError::Unavailable(error)) => {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"{error}. External-change checks remain active." "{error}. External-change checks remain active."
)); ).as_ref());
} }
} }
} }
@ -1424,9 +1427,9 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
.file_name() .file_name()
.map(|name| name.to_string_lossy().into_owned()) .map(|name| name.to_string_lossy().into_owned())
.unwrap_or_else(|| outcome.path.display().to_string()); .unwrap_or_else(|| outcome.path.display().to_string());
self.command_line.push_error_once(&format!( self.command_line.push_error_once(crate::tf!(
"Save stopped: \"{file_name}\" changed outside Open CAD Studio." "Save stopped: \"{file_name}\" changed outside Open CAD Studio."
)); ).as_ref());
self.pending_external_change = Some(crate::app::PendingExternalChange { self.pending_external_change = Some(crate::app::PendingExternalChange {
tab_id: outcome.tab_id, tab_id: outcome.tab_id,
path: outcome.path.clone(), path: outcome.path.clone(),
@ -1445,9 +1448,9 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
.file_name() .file_name()
.map(|name| name.to_string_lossy().into_owned()) .map(|name| name.to_string_lossy().into_owned())
.unwrap_or_else(|| outcome.path.display().to_string()); .unwrap_or_else(|| outcome.path.display().to_string());
self.command_line.push_error_once(&format!( self.command_line.push_error_once(crate::tf!(
"Unable to save \"{file_name}\": file is in use by another application." "Unable to save \"{file_name}\": file is in use by another application."
)); ).as_ref());
self.pending_save_failure = Some(crate::app::PendingSaveFailure { self.pending_save_failure = Some(crate::app::PendingSaveFailure {
tab_id: outcome.tab_id, tab_id: outcome.tab_id,
path: outcome.path.clone(), path: outcome.path.clone(),
@ -1470,7 +1473,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
return Task::none(); return Task::none();
} }
self.command_line self.command_line
.push_error(&format!("Save failed: {error}")); .push_error(crate::tf!("Save failed: {error}").as_ref());
return match outcome.continuation { return match outcome.continuation {
crate::app::SaveContinuation::CloseTab => { crate::app::SaveContinuation::CloseTab => {
self.pending_close = Some(crate::app::PendingClose::Tab(i)); self.pending_close = Some(crate::app::PendingClose::Tab(i));
@ -1496,7 +1499,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
let mut tasks = Vec::new(); let mut tasks = Vec::new();
match outcome.purpose { match outcome.purpose {
crate::app::SavePurpose::Autosave => { crate::app::SavePurpose::Autosave => {
self.command_line.push_output("Autosaved 1 drawing"); self.command_line.push_output(crate::t!("Autosaved 1 drawing").as_ref());
} }
crate::app::SavePurpose::Manual | crate::app::SavePurpose::SaveAs => { crate::app::SavePurpose::Manual | crate::app::SavePurpose::SaveAs => {
let path_changed = outcome.set_current_path let path_changed = outcome.set_current_path
@ -1507,7 +1510,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
!native_paths_match(current, &outcome.path) !native_paths_match(current, &outcome.path)
}); });
self.command_line self.command_line
.push_output(&format!("Saved: {}", outcome.path.display())); .push_output(crate::tf!("Saved: {}", outcome.path.display()).as_ref());
self.recent_thumbs.remove(&outcome.path); self.recent_thumbs.remove(&outcome.path);
if let Some(previous) = outcome.previous_autosave { if let Some(previous) = outcome.previous_autosave {
if previous != outcome.path { if previous != outcome.path {
@ -1594,7 +1597,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
self.pending_save_failure = Some(failure); self.pending_save_failure = Some(failure);
self.restore_failed_save_continuation(continuation, i); self.restore_failed_save_continuation(continuation, i);
self.command_line self.command_line
.push_error_once(&format!("Unable to acquire edit lock: {error}")); .push_error_once(crate::tf!("Unable to acquire edit lock: {error}").as_ref());
self.active_modal = Some(crate::app::ModalKind::FileInUse); self.active_modal = Some(crate::app::ModalKind::FileInUse);
return Task::none(); return Task::none();
} }
@ -1690,7 +1693,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
Err(error) => { Err(error) => {
self.close_active_modal(); self.close_active_modal();
self.command_line self.command_line
.push_error(&format!("Reload failed: {error}")); .push_error(crate::tf!("Reload failed: {error}").as_ref());
return Task::none(); return Task::none();
} }
}; };
@ -1709,7 +1712,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
pub(super) fn on_save_file(&mut self) -> Task<Message> { pub(super) fn on_save_file(&mut self) -> Task<Message> {
if self.read_only { if self.read_only {
self.command_line self.command_line
.push_error("Read-only session (--read-only): saving is disabled."); .push_error(crate::t!("Read-only session (--read-only): saving is disabled.").as_ref());
return Task::none(); return Task::none();
} }
let i = self.active_tab; let i = self.active_tab;
@ -1859,11 +1862,11 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
}, },
Message::WebRecentStored, Message::WebRecentStored,
); );
self.command_line.push_output(&format!("Saved: {filename}")); self.command_line.push_output(crate::tf!("Saved: {filename}").as_ref());
true true
} }
Err(e) => { Err(e) => {
self.command_line.push_error(&format!("Save failed: {e}")); self.command_line.push_error(crate::tf!("Save failed: {e}").as_ref());
false false
} }
}; };
@ -2163,10 +2166,10 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.tabs[i].scene.bump_geometry_no_blocks(); self.tabs[i].scene.bump_geometry_no_blocks();
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"Page setup: {w:.1}×{h:.1} mm area={plot_area} \ "Page setup: {w:.1}×{h:.1} mm area={plot_area} \
center={center} rot={rotation}°" center={center} rot={rotation}°"
)); ).as_ref());
} }
} }
@ -2178,7 +2181,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
self.direct_plot_params() self.direct_plot_params()
else { else {
self.command_line self.command_line
.push_error("Nothing to plot: model space contains no printable geometry."); .push_error(crate::t!("Nothing to plot: model space contains no printable geometry.").as_ref());
return Task::none(); return Task::none();
}; };
let plot_style = self.dialog_plot_style(&self.plot_dialog); let plot_style = self.dialog_plot_style(&self.plot_dialog);
@ -2221,7 +2224,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
job job
else { else {
self.command_line self.command_line
.push_error("Plot area is empty. Pick a larger window."); .push_error(crate::t!("Plot area is empty. Pick a larger window.").as_ref());
return Task::none(); return Task::none();
}; };
let plot_style = self.dialog_plot_style(&self.plot_dialog); let plot_style = self.dialog_plot_style(&self.plot_dialog);
@ -2533,12 +2536,12 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
self.direct_plot_params() self.direct_plot_params()
else { else {
self.command_line self.command_line
.push_error("Nothing to plot: model space contains no printable geometry."); .push_error(crate::t!("Nothing to plot: model space contains no printable geometry.").as_ref());
return Task::none(); return Task::none();
}; };
let plot_style = self.dialog_plot_style(&self.plot_dialog); let plot_style = self.dialog_plot_style(&self.plot_dialog);
let options = self.plot_print_options(&self.plot_dialog, group_splits); let options = self.plot_print_options(&self.plot_dialog, group_splits);
self.command_line.push_info("Sending to system printer…"); self.command_line.push_info(crate::t!("Sending to system printer…").as_ref());
background_task( background_task(
move || { move || {
iced::futures::executor::block_on( iced::futures::executor::block_on(
@ -2561,7 +2564,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
let i = self.active_tab; let i = self.active_tab;
if self.tabs[i].scene.current_layout != "Model" { if self.tabs[i].scene.current_layout != "Model" {
self.command_line self.command_line
.push_error("Quick print works in model space."); .push_error(crate::t!("Quick print works in model space.").as_ref());
return Task::none(); return Task::none();
} }
let set: std::collections::HashSet<acadrust::Handle> = handles.into_iter().collect(); let set: std::collections::HashSet<acadrust::Handle> = handles.into_iter().collect();
@ -2593,12 +2596,12 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
}; };
if !any { if !any {
self.command_line self.command_line
.push_error("Selection has no printable geometry."); .push_error(crate::t!("Selection has no printable geometry.").as_ref());
return Task::none(); return Task::none();
} }
if !(x1 > x0 && y1 > y0) { if !(x1 > x0 && y1 > y0) {
self.command_line self.command_line
.push_error("Selection has no printable area."); .push_error(crate::t!("Selection has no printable area.").as_ref());
return Task::none(); return Task::none();
} }
// Small margin so the outermost strokes aren't clipped flush to the edge. // Small margin so the outermost strokes aren't clipped flush to the edge.
@ -2786,7 +2789,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
match crate::io::print_to_printer::open_printer_properties( match crate::io::print_to_printer::open_printer_properties(
self.plot_dialog.printer.as_deref(), self.plot_dialog.printer.as_deref(),
) { ) {
Ok(()) => self.command_line.push_info("Opened printer properties."), Ok(()) => self.command_line.push_info(crate::t!("Opened printer properties.").as_ref()),
Err(error) => self.command_line.push_error(&error), Err(error) => self.command_line.push_error(&error),
} }
Task::none() Task::none()
@ -2924,7 +2927,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
} }
M::SetCurrent => { M::SetCurrent => {
self.apply_dialog_to_layout(); self.apply_dialog_to_layout();
self.command_line.push_info("Page setup applied to the layout."); self.command_line.push_info(crate::t!("Page setup applied to the layout.").as_ref());
Task::none() Task::none()
} }
M::NewSetup => { M::NewSetup => {
@ -3327,10 +3330,10 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
fn on_plot_dlg_commit(&mut self, preview: bool) -> Task<Message> { fn on_plot_dlg_commit(&mut self, preview: bool) -> Task<Message> {
let d = self.plot_dialog.clone(); let d = self.plot_dialog.clone();
if d.style_missing { if d.style_missing {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"Plot style table '{}' is not loaded.", "Plot style table '{}' is not loaded.",
d.style_name d.style_name
)); ).as_ref());
return Task::none(); return Task::none();
} }
// Remember the user's print preferences across sessions. // Remember the user's print preferences across sessions.
@ -3365,7 +3368,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
)) = job )) = job
else { else {
self.command_line self.command_line
.push_error("Plot area is empty. Pick a larger window."); .push_error(crate::t!("Plot area is empty. Pick a larger window.").as_ref());
self.active_modal = Some(crate::app::ModalKind::Plot); self.active_modal = Some(crate::app::ModalKind::Plot);
return Task::none(); return Task::none();
}; };
@ -3436,7 +3439,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
} }
let opts = self.plot_print_options(&d, group_splits); let opts = self.plot_print_options(&d, group_splits);
self.command_line.push_info("Sending to system printer…"); self.command_line.push_info(crate::t!("Sending to system printer…").as_ref());
let work = move || { let work = move || {
iced::futures::executor::block_on( iced::futures::executor::block_on(
crate::io::print_to_printer::print_wires_with( crate::io::print_to_printer::print_wires_with(
@ -3692,7 +3695,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
entry.screening = sc.min(100); entry.screening = sc.min(100);
} }
self.command_line self.command_line
.push_output(&format!("Plot style ACI {aci} updated.")); .push_output(crate::tf!("Plot style ACI {aci} updated.").as_ref());
} }
} else { } else {
// No table loaded: create an identity table and apply. // No table loaded: create an identity table and apply.
@ -3714,7 +3717,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
} }
self.active_plot_style = Some(table); self.active_plot_style = Some(table);
self.command_line self.command_line
.push_output(&format!("Created new CTB table, ACI {aci} updated.")); .push_output(crate::tf!("Created new CTB table, ACI {aci} updated.").as_ref());
} }
Task::none() Task::none()
} }
@ -3722,7 +3725,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
pub(super) fn on_plot_style_panel_save(&mut self) -> Task<Message> { pub(super) fn on_plot_style_panel_save(&mut self) -> Task<Message> {
if self.active_plot_style.is_none() { if self.active_plot_style.is_none() {
self.command_line self.command_line
.push_error("No plot style table loaded. Load or create one first."); .push_error(crate::t!("No plot style table loaded. Load or create one first.").as_ref());
return Task::none(); return Task::none();
} }
let default_name = self let default_name = self

View file

@ -284,7 +284,7 @@ impl OpenCADStudio {
Err(e) => { Err(e) => {
crate::scene::text::web_font::insert(script, None); crate::scene::text::web_font::insert(script, None);
self.command_line self.command_line
.push_error(&format!("Font load failed ({script:?}): {e}")); .push_error(crate::tf!("Font load failed ({script:?}): {e}").as_ref());
} }
} }
Task::none() Task::none()
@ -323,10 +323,10 @@ impl OpenCADStudio {
return match std::fs::metadata(&path) { return match std::fs::metadata(&path) {
Ok(m) => self.update(Message::OpenPathPicked(Some((path, m.len())))), Ok(m) => self.update(Message::OpenPathPicked(Some((path, m.len())))),
Err(_) => { Err(_) => {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"Recent file no longer exists: {}", "Recent file no longer exists: {}",
path.display() path.display()
)); ).as_ref());
Task::none() Task::none()
} }
}; };
@ -489,7 +489,7 @@ impl OpenCADStudio {
.map(|(_, _, l)| *l) .map(|(_, _, l)| *l)
.unwrap_or("Snap"); .unwrap_or("Snap");
self.command_line self.command_line
.push_info(&format!("Snap override: {label} (next pick only).")); .push_info(crate::tf!("Snap override: {label} (next pick only).").as_ref());
Task::none() Task::none()
} }
@ -507,10 +507,10 @@ impl OpenCADStudio {
.map(|e| e.to_string_lossy().to_lowercase()) .map(|e| e.to_string_lossy().to_lowercase())
.unwrap_or_default(); .unwrap_or_default();
if !matches!(ext.as_str(), "dwg" | "dxf" | "bak" | "sv$") { if !matches!(ext.as_str(), "dwg" | "dxf" | "bak" | "sv$") {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"Unsupported file type: {}", "Unsupported file type: {}",
path.display() path.display()
)); ).as_ref());
return Task::none(); return Task::none();
} }
// Already open → switch to its tab; a load in progress → // Already open → switch to its tab; a load in progress →
@ -563,7 +563,7 @@ impl OpenCADStudio {
}); });
let size_label = format_size(size_bytes); let size_label = format_size(size_bytes);
self.command_line self.command_line
.push_info(&format!("Opening \"{name}\" ({size_label})…")); .push_info(crate::tf!("Opening \"{name}\" ({size_label})…").as_ref());
let model_bg = self.default_bg_color.unwrap_or([ let model_bg = self.default_bg_color.unwrap_or([
33.0 / 255.0, 33.0 / 255.0,
40.0 / 255.0, 40.0 / 255.0,
@ -579,7 +579,7 @@ impl OpenCADStudio {
Message::OpenCancel => { Message::OpenCancel => {
if let Some(p) = self.opening.take() { if let Some(p) = self.opening.take() {
self.command_line self.command_line
.push_info(&format!("Open cancelled: \"{}\"", p.name)); .push_info(crate::tf!("Open cancelled: \"{}\"", p.name).as_ref());
} }
self.drain_pending_open() self.drain_pending_open()
} }
@ -593,7 +593,7 @@ impl OpenCADStudio {
// we suppress the noise. // we suppress the noise.
let was_open = self.opening.take().is_some(); let was_open = self.opening.take().is_some();
if was_open && e != "Cancelled" { if was_open && e != "Cancelled" {
self.command_line.push_error(&format!("Open failed: {e}")); self.command_line.push_error(crate::tf!("Open failed: {e}").as_ref());
} }
// A drawing that fails to parse must not strand the ones queued // A drawing that fails to parse must not strand the ones queued
// behind it. // behind it.
@ -604,9 +604,9 @@ impl OpenCADStudio {
Message::WebRecentStored(result) => match result { Message::WebRecentStored(result) => match result {
Ok(path) => self.push_recent(path), Ok(path) => self.push_recent(path),
Err(error) => { Err(error) => {
self.command_line.push_error(&format!( self.command_line.push_error(crate::tf!(
"Saved download, but recent copy could not be stored: {error}" "Saved download, but recent copy could not be stored: {error}"
)); ).as_ref());
Task::none() Task::none()
} }
}, },
@ -625,7 +625,7 @@ impl OpenCADStudio {
.unwrap_or(&path_str) .unwrap_or(&path_str)
.to_string(); .to_string();
self.command_line self.command_line
.push_output(&format!("IMAGE \"{short}\": {pw}×{ph} px")); .push_output(crate::tf!("IMAGE \"{short}\": {pw}×{ph} px").as_ref());
let cmd = ImageCommand::new(path_str, pw, ph); let cmd = ImageCommand::new(path_str, pw, ph);
let i = self.active_tab; let i = self.active_tab;
self.command_line.push_info(&cmd.prompt()); self.command_line.push_info(&cmd.prompt());
@ -635,7 +635,7 @@ impl OpenCADStudio {
Message::ImagePickResult(Err(e)) => { Message::ImagePickResult(Err(e)) => {
if e != "Cancelled" { if e != "Cancelled" {
self.command_line.push_error(&format!("IMAGE: {e}")); self.command_line.push_error(crate::tf!("IMAGE: {e}").as_ref());
} }
Task::none() Task::none()
} }
@ -671,7 +671,7 @@ impl OpenCADStudio {
Message::XAttachPickResult(Err(e)) => { Message::XAttachPickResult(Err(e)) => {
if e != "Cancelled" { if e != "Cancelled" {
self.command_line.push_error(&format!("XATTACH: {e}")); self.command_line.push_error(crate::tf!("XATTACH: {e}").as_ref());
} }
Task::none() Task::none()
} }
@ -701,13 +701,13 @@ impl OpenCADStudio {
Message::WblockWriteFinished(block_name, path, result) => { Message::WblockWriteFinished(block_name, path, result) => {
match result { match result {
Ok(()) => self.command_line.push_output(&format!( Ok(()) => self.command_line.push_output(crate::tf!(
"WBLOCK Saved \"{block_name}\" → \"{}\"", "WBLOCK Saved \"{block_name}\" → \"{}\"",
path.display() path.display()
)), ).as_ref()),
Err(error) => self Err(error) => self
.command_line .command_line
.push_error(&format!("WBLOCK save failed: {error}")), .push_error(crate::tf!("WBLOCK save failed: {error}").as_ref()),
} }
Task::none() Task::none()
} }
@ -739,11 +739,11 @@ impl OpenCADStudio {
.map(|n| n.to_string_lossy().into_owned()) .map(|n| n.to_string_lossy().into_owned())
.unwrap_or_else(|| path.to_string_lossy().into_owned()); .unwrap_or_else(|| path.to_string_lossy().into_owned());
self.command_line self.command_line
.push_output(&format!("DATAEXTRACTION {rows} rows → \"{fname}\"")); .push_output(crate::tf!("DATAEXTRACTION {rows} rows → \"{fname}\"").as_ref());
} }
Err(e) => self Err(e) => self
.command_line .command_line
.push_error(&format!("DATAEXTRACTION: write failed: {e}")), .push_error(crate::tf!("DATAEXTRACTION: write failed: {e}").as_ref()),
} }
Task::none() Task::none()
} }
@ -754,7 +754,7 @@ impl OpenCADStudio {
let i = self.active_tab; let i = self.active_tab;
if self.tabs[i].scene.meshes.is_empty() { if self.tabs[i].scene.meshes.is_empty() {
self.command_line self.command_line
.push_error("STLOUT: no 3D mesh data in this drawing."); .push_error(crate::t!("STLOUT: no 3D mesh data in this drawing.").as_ref());
return Task::none(); return Task::none();
} }
Task::perform( Task::perform(
@ -780,8 +780,8 @@ impl OpenCADStudio {
match result { match result {
Ok(()) => self Ok(()) => self
.command_line .command_line
.push_output(&format!("STLOUT: exported to \"{}\"", path.display())), .push_output(crate::tf!("STLOUT: exported to \"{}\"", path.display()).as_ref()),
Err(error) => self.command_line.push_error(&format!("STLOUT: {error}")), Err(error) => self.command_line.push_error(crate::tf!("STLOUT: {error}").as_ref()),
} }
Task::none() Task::none()
} }
@ -791,7 +791,7 @@ impl OpenCADStudio {
let i = self.active_tab; let i = self.active_tab;
if self.tabs[i].scene.meshes.is_empty() { if self.tabs[i].scene.meshes.is_empty() {
self.command_line self.command_line
.push_error("STEPOUT: no 3D mesh data in this drawing."); .push_error(crate::t!("STEPOUT: no 3D mesh data in this drawing.").as_ref());
return Task::none(); return Task::none();
} }
Task::perform( Task::perform(
@ -817,8 +817,8 @@ impl OpenCADStudio {
match result { match result {
Ok(()) => self Ok(()) => self
.command_line .command_line
.push_output(&format!("STEPOUT: exported to \"{}\"", path.display())), .push_output(crate::tf!("STEPOUT: exported to \"{}\"", path.display()).as_ref()),
Err(error) => self.command_line.push_error(&format!("STEPOUT: {error}")), Err(error) => self.command_line.push_error(crate::tf!("STEPOUT: {error}").as_ref()),
} }
Task::none() Task::none()
} }
@ -843,11 +843,11 @@ impl OpenCADStudio {
Message::ObjImportFinished(tab_id, path, result) => { Message::ObjImportFinished(tab_id, path, result) => {
match result { match result {
Err(error) => self.command_line.push_error(&format!("IMPORTOBJ: {error}")), Err(error) => self.command_line.push_error(crate::tf!("IMPORTOBJ: {error}").as_ref()),
Ok(mut mesh) => { Ok(mut mesh) => {
let Some(i) = self.tabs.iter().position(|tab| tab.id == tab_id) else { let Some(i) = self.tabs.iter().position(|tab| tab.id == tab_id) else {
self.command_line self.command_line
.push_info("IMPORTOBJ: target drawing was closed."); .push_info(crate::t!("IMPORTOBJ: target drawing was closed.").as_ref());
return Task::none(); return Task::none();
}; };
let file_stem = path let file_stem = path
@ -864,9 +864,9 @@ impl OpenCADStudio {
.meshes .meshes
.insert(handle, crate::scene::MeshLodSet::from_single(mesh)); .insert(handle, crate::scene::MeshLodSet::from_single(mesh));
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"IMPORTOBJ: imported \"{file_stem}\" as mesh." "IMPORTOBJ: imported \"{file_stem}\" as mesh."
)); ).as_ref());
} }
} }
} }
@ -878,7 +878,7 @@ impl OpenCADStudio {
Message::SaveAs => { Message::SaveAs => {
if self.read_only { if self.read_only {
self.command_line self.command_line
.push_error("Read-only session (--read-only): saving is disabled."); .push_error(crate::t!("Read-only session (--read-only): saving is disabled.").as_ref());
return Task::none(); return Task::none();
} }
let i = self.active_tab; let i = self.active_tab;
@ -924,7 +924,7 @@ impl OpenCADStudio {
.layers .layers
.sync_with_viewports(&doc_layers, vp_info); .sync_with_viewports(&doc_layers, vp_info);
self.command_line self.command_line
.push_output("Scene cleared. Standard linetypes loaded."); .push_output(crate::t!("Scene cleared. Standard linetypes loaded.").as_ref());
self.tabs[i].current_path = None; self.tabs[i].current_path = None;
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.sync_ribbon_layers(); self.sync_ribbon_layers();
@ -988,7 +988,7 @@ impl OpenCADStudio {
let i = self.active_tab; let i = self.active_tab;
if !crate::plugin::try_dispatch(self, i, &line) { if !crate::plugin::try_dispatch(self, i, &line) {
self.command_line self.command_line
.push_error(&format!("No plugin handled: {command}")); .push_error(crate::tf!("No plugin handled: {command}").as_ref());
} }
} }
Task::none() Task::none()
@ -1138,7 +1138,7 @@ impl OpenCADStudio {
let Some(path) = self.tabs.get(idx).and_then(|tab| tab.current_path.clone()) let Some(path) = self.tabs.get(idx).and_then(|tab| tab.current_path.clone())
else { else {
self.command_line self.command_line
.push_error("Save the drawing before copying its file path."); .push_error(crate::t!("Save the drawing before copying its file path.").as_ref());
return Task::none(); return Task::none();
}; };
let full_path = path.canonicalize().unwrap_or_else(|_| { let full_path = path.canonicalize().unwrap_or_else(|_| {
@ -1151,7 +1151,7 @@ impl OpenCADStudio {
} }
}); });
self.command_line self.command_line
.push_output(&format!("Copied path: {}", full_path.display())); .push_output(crate::tf!("Copied path: {}", full_path.display()).as_ref());
return iced::clipboard::write(full_path.to_string_lossy().into_owned()) return iced::clipboard::write(full_path.to_string_lossy().into_owned())
.discard(); .discard();
} }
@ -1159,7 +1159,7 @@ impl OpenCADStudio {
{ {
let _ = idx; let _ = idx;
self.command_line self.command_line
.push_error("Full file paths are unavailable in the web application."); .push_error(crate::t!("Full file paths are unavailable in the web application.").as_ref());
Task::none() Task::none()
} }
} }
@ -1170,16 +1170,16 @@ impl OpenCADStudio {
let Some(path) = self.tabs.get(idx).and_then(|tab| tab.current_path.clone()) let Some(path) = self.tabs.get(idx).and_then(|tab| tab.current_path.clone())
else { else {
self.command_line self.command_line
.push_error("Save the drawing before opening its file location."); .push_error(crate::t!("Save the drawing before opening its file location.").as_ref());
return Task::none(); return Task::none();
}; };
match crate::sys::reveal_in_file_manager(&path) { match crate::sys::reveal_in_file_manager(&path) {
Ok(()) => self Ok(()) => self
.command_line .command_line
.push_output(&format!("Opened file location: {}", path.display())), .push_output(crate::tf!("Opened file location: {}", path.display()).as_ref()),
Err(error) => self Err(error) => self
.command_line .command_line
.push_error(&format!("Could not open file location: {error}")), .push_error(crate::tf!("Could not open file location: {error}").as_ref()),
} }
Task::none() Task::none()
} }
@ -1187,7 +1187,7 @@ impl OpenCADStudio {
{ {
let _ = idx; let _ = idx;
self.command_line self.command_line
.push_error("File locations are unavailable in the web application."); .push_error(crate::t!("File locations are unavailable in the web application.").as_ref());
Task::none() Task::none()
} }
} }
@ -1442,7 +1442,7 @@ impl OpenCADStudio {
self.ribbon.close_dropdown(); self.ribbon.close_dropdown();
if self.tabs[i].is_start { if self.tabs[i].is_start {
self.command_line self.command_line
.push_info("Open or create a drawing to manage layer states."); .push_info(crate::t!("Open or create a drawing to manage layer states.").as_ref());
return Task::none(); return Task::none();
} }
let mut names: Vec<String> = self.tabs[i] let mut names: Vec<String> = self.tabs[i]
@ -1482,7 +1482,7 @@ impl OpenCADStudio {
let name = self.layer_state_name_buf.trim().to_string(); let name = self.layer_state_name_buf.trim().to_string();
if name.is_empty() { if name.is_empty() {
self.command_line self.command_line
.push_error("Layer state name cannot be empty."); .push_error(crate::t!("Layer state name cannot be empty.").as_ref());
return Task::none(); return Task::none();
} }
let old_name = self.layer_state_selected.clone(); let old_name = self.layer_state_selected.clone();
@ -1499,7 +1499,7 @@ impl OpenCADStudio {
}); });
if duplicate { if duplicate {
self.command_line self.command_line
.push_error(&format!("Layer state \"{name}\" already exists.")); .push_error(crate::tf!("Layer state \"{name}\" already exists.").as_ref());
return Task::none(); return Task::none();
} }
@ -1520,7 +1520,7 @@ impl OpenCADStudio {
self.layer_state_selected = Some(name.clone()); self.layer_state_selected = Some(name.clone());
self.layer_state_name_buf = name.clone(); self.layer_state_name_buf = name.clone();
self.command_line self.command_line
.push_output(&format!("LAYERSTATE: saved \"{name}\" in the drawing.")); .push_output(crate::tf!("LAYERSTATE: saved \"{name}\" in the drawing.").as_ref());
Task::none() Task::none()
} }
Message::LayerStateManagerRestore => { Message::LayerStateManagerRestore => {
@ -1553,9 +1553,9 @@ impl OpenCADStudio {
.invalidate_layer_dependencies(&layer_names); .invalidate_layer_dependencies(&layer_names);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.refresh_layer_panel(); self.refresh_layer_panel();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"LAYERSTATE: restored \"{name}\" ({restored} layer(s))." "LAYERSTATE: restored \"{name}\" ({restored} layer(s))."
)); ).as_ref());
Task::none() Task::none()
} }
Message::LayerStateManagerDelete => { Message::LayerStateManagerDelete => {
@ -1576,7 +1576,7 @@ impl OpenCADStudio {
names.sort_by_key(|name| name.to_lowercase()); names.sort_by_key(|name| name.to_lowercase());
self.load_layer_state_editor(names.into_iter().next()); self.load_layer_state_editor(names.into_iter().next());
self.command_line self.command_line
.push_output(&format!("LAYERSTATE: deleted \"{name}\".")); .push_output(crate::tf!("LAYERSTATE: deleted \"{name}\".").as_ref());
} }
Task::none() Task::none()
} }
@ -1587,7 +1587,7 @@ impl OpenCADStudio {
}; };
let Some(state) = self.tabs[i].scene.document.layer_state(&name) else { let Some(state) = self.tabs[i].scene.document.layer_state(&name) else {
self.command_line self.command_line
.push_error(&format!("Layer state \"{name}\" was not found.")); .push_error(crate::tf!("Layer state \"{name}\" was not found.").as_ref());
return Task::none(); return Task::none();
}; };
self.layer_state_edit_draft = Some(state); self.layer_state_edit_draft = Some(state);
@ -1730,7 +1730,7 @@ impl OpenCADStudio {
let name = draft.name.trim().to_string(); let name = draft.name.trim().to_string();
if name.is_empty() { if name.is_empty() {
self.command_line self.command_line
.push_error("Layer state name cannot be empty."); .push_error(crate::t!("Layer state name cannot be empty.").as_ref());
return Task::none(); return Task::none();
} }
let old_name = self.layer_state_selected.clone(); let old_name = self.layer_state_selected.clone();
@ -1747,7 +1747,7 @@ impl OpenCADStudio {
}); });
if duplicate { if duplicate {
self.command_line self.command_line
.push_error(&format!("Layer state \"{name}\" already exists.")); .push_error(crate::tf!("Layer state \"{name}\" already exists.").as_ref());
return Task::none(); return Task::none();
} }
let Some(state) = self.layer_state_edit_draft.take() else { let Some(state) = self.layer_state_edit_draft.take() else {
@ -1775,7 +1775,7 @@ impl OpenCADStudio {
self.layer_state_edit_color_open = None; self.layer_state_edit_color_open = None;
self.active_modal = Some(super::ModalKind::LayerStateManager); self.active_modal = Some(super::ModalKind::LayerStateManager);
self.command_line self.command_line
.push_output(&format!("LAYERSTATE: updated \"{name}\".")); .push_output(crate::tf!("LAYERSTATE: updated \"{name}\".").as_ref());
Task::none() Task::none()
} }
Message::LayerStateEditorCancel => { Message::LayerStateEditorCancel => {
@ -1829,11 +1829,11 @@ impl OpenCADStudio {
self.tabs[i].scene.invalidate_layer_dependencies(&targets); self.tabs[i].scene.invalidate_layer_dependencies(&targets);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.commit_layer_undo(i, undo); self.commit_layer_undo(i, undo);
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"{} layer(s) turned {}", "{} layer(s) turned {}",
targets.len(), targets.len(),
if on { "on" } else { "off" } if on { "on" } else { "off" }
)); ).as_ref());
self.sync_ribbon_layers(); self.sync_ribbon_layers();
} }
} }
@ -1869,11 +1869,11 @@ impl OpenCADStudio {
// Lock state affects editability, not rendered geometry. // Lock state affects editability, not rendered geometry.
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.commit_layer_undo(i, undo); self.commit_layer_undo(i, undo);
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"{} layer(s) {}", "{} layer(s) {}",
targets.len(), targets.len(),
if locked { "locked" } else { "unlocked" } if locked { "locked" } else { "unlocked" }
)); ).as_ref());
self.sync_ribbon_layers(); self.sync_ribbon_layers();
} }
} }
@ -1904,11 +1904,11 @@ impl OpenCADStudio {
self.tabs[i].scene.invalidate_layer_dependencies(&targets); self.tabs[i].scene.invalidate_layer_dependencies(&targets);
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.commit_layer_undo(i, undo); self.commit_layer_undo(i, undo);
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"{} layer(s) {}", "{} layer(s) {}",
targets.len(), targets.len(),
if frozen { "frozen" } else { "thawed" } if frozen { "frozen" } else { "thawed" }
)); ).as_ref());
self.sync_ribbon_layers(); self.sync_ribbon_layers();
} }
} }
@ -2300,7 +2300,7 @@ impl OpenCADStudio {
self.tabs[i].scene.camera.borrow_mut().home_view(r_ucs); self.tabs[i].scene.camera.borrow_mut().home_view(r_ucs);
} }
self.tabs[i].scene.camera_generation += 1; self.tabs[i].scene.camera_generation += 1;
self.command_line.push_output("View: Home"); self.command_line.push_output(crate::t!("View: Home").as_ref());
Task::none() Task::none()
} }
@ -2351,11 +2351,11 @@ impl OpenCADStudio {
let mut changed = false; let mut changed = false;
if name.is_empty() || name == "WCS" { if name.is_empty() || name == "WCS" {
self.tabs[i].active_ucs = None; self.tabs[i].active_ucs = None;
self.command_line.push_output("UCS: World"); self.command_line.push_output(crate::t!("UCS: World").as_ref());
changed = true; changed = true;
} else if let Some(named) = self.tabs[i].scene.document.ucss.get(&name).cloned() { } else if let Some(named) = self.tabs[i].scene.document.ucss.get(&name).cloned() {
self.tabs[i].active_ucs = Some(named); self.tabs[i].active_ucs = Some(named);
self.command_line.push_output(&format!("UCS: {}", name)); self.command_line.push_output(crate::tf!("UCS: {}", name).as_ref());
changed = true; changed = true;
} }
if changed { if changed {
@ -2511,7 +2511,7 @@ impl OpenCADStudio {
_ => "live", _ => "live",
}; };
self.command_line self.command_line
.push_output(&format!("COORDS = {mode} ({label})")); .push_output(crate::tf!("COORDS = {mode} ({label})").as_ref());
} }
Task::none() Task::none()
} }
@ -2700,11 +2700,11 @@ impl OpenCADStudio {
self.active_modal = Some(crate::app::ModalKind::AnnoObjectScale); self.active_modal = Some(crate::app::ModalKind::AnnoObjectScale);
} else { } else {
self.command_line self.command_line
.push_info("The selected object does not support annotation scales."); .push_info(crate::t!("The selected object does not support annotation scales.").as_ref());
} }
} else { } else {
self.command_line self.command_line
.push_info("Select one object first, then run OBJECTSCALE."); .push_info(crate::t!("Select one object first, then run OBJECTSCALE.").as_ref());
} }
Task::none() Task::none()
} }
@ -3341,7 +3341,7 @@ impl OpenCADStudio {
let to_move: Vec<_> = self.tabs[i].scene.selected.iter().cloned().collect(); let to_move: Vec<_> = self.tabs[i].scene.selected.iter().cloned().collect();
if to_move.is_empty() { if to_move.is_empty() {
self.command_line self.command_line
.push_error("DRAWORDER: select entities first."); .push_error(crate::t!("DRAWORDER: select entities first.").as_ref());
} else { } else {
use crate::command::CadCommand; use crate::command::CadCommand;
let cmd = super::commands::DrawOrderRefCommand::new(to_move, above); let cmd = super::commands::DrawOrderRefCommand::new(to_move, above);
@ -3356,7 +3356,7 @@ impl OpenCADStudio {
self.tabs[i].scene.selection.borrow_mut().context_menu = None; self.tabs[i].scene.selection.borrow_mut().context_menu = None;
let added = self.tabs[i].scene.select_similar(); let added = self.tabs[i].scene.select_similar();
self.command_line self.command_line
.push_output(&format!("Select Similar: {} added.", added)); .push_output(crate::tf!("Select Similar: {} added.", added).as_ref());
self.refresh_properties(); self.refresh_properties();
Task::none() Task::none()
} }
@ -3366,7 +3366,7 @@ impl OpenCADStudio {
self.tabs[i].scene.selection.borrow_mut().context_menu = None; self.tabs[i].scene.selection.borrow_mut().context_menu = None;
let count = self.tabs[i].scene.invert_selection(); let count = self.tabs[i].scene.invert_selection();
self.command_line self.command_line
.push_output(&format!("Invert Selection: {} object(s) selected.", count)); .push_output(crate::tf!("Invert Selection: {} object(s) selected.", count).as_ref());
self.refresh_properties(); self.refresh_properties();
Task::none() Task::none()
} }
@ -3441,7 +3441,7 @@ impl OpenCADStudio {
state.append, state.append,
); );
self.command_line self.command_line
.push_output(&format!("QSELECT: {} object(s) selected.", matched)); .push_output(crate::tf!("QSELECT: {} object(s) selected.", matched).as_ref());
self.refresh_properties(); self.refresh_properties();
Task::none() Task::none()
} }
@ -4023,7 +4023,7 @@ impl OpenCADStudio {
if self.tabs[i].scene.delete_layout(&name) { if self.tabs[i].scene.delete_layout(&name) {
self.layout_rename_state = None; self.layout_rename_state = None;
self.command_line self.command_line
.push_output(&format!("Layout \"{name}\" silindi")); .push_output(crate::tf!("Layout \"{name}\" silindi").as_ref());
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
} }
Task::batch([cancel_task, switch_task]) Task::batch([cancel_task, switch_task])
@ -4061,7 +4061,7 @@ impl OpenCADStudio {
let i = self.active_tab; let i = self.active_tab;
if self.tabs[i].is_start { if self.tabs[i].is_start {
self.command_line self.command_line
.push_info("Open or create a drawing to manage layouts."); .push_info(crate::t!("Open or create a drawing to manage layouts.").as_ref());
return Task::none(); return Task::none();
} }
let current = self.tabs[i].scene.current_layout.clone(); let current = self.tabs[i].scene.current_layout.clone();
@ -4097,9 +4097,9 @@ impl OpenCADStudio {
let new_name = self.layout_manager_rename_buf.trim().to_string(); let new_name = self.layout_manager_rename_buf.trim().to_string();
if old_name == "Model" { if old_name == "Model" {
self.command_line self.command_line
.push_error("Cannot rename the Model layout."); .push_error(crate::t!("Cannot rename the Model layout.").as_ref());
} else if new_name.is_empty() { } else if new_name.is_empty() {
self.command_line.push_error("Layout name cannot be empty."); self.command_line.push_error(crate::t!("Layout name cannot be empty.").as_ref());
} else if new_name == old_name { } else if new_name == old_name {
// no-op // no-op
} else { } else {
@ -4111,7 +4111,7 @@ impl OpenCADStudio {
self.layout_manager_selected = new_name.clone(); self.layout_manager_selected = new_name.clone();
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("Layout renamed: '{old_name}' → '{new_name}'")); .push_output(crate::tf!("Layout renamed: '{old_name}' → '{new_name}'").as_ref());
} }
Task::none() Task::none()
} }
@ -4119,7 +4119,7 @@ impl OpenCADStudio {
let i = self.active_tab; let i = self.active_tab;
if self.tabs[i].is_start { if self.tabs[i].is_start {
self.command_line self.command_line
.push_info("Open or create a drawing to add a layout."); .push_info(crate::t!("Open or create a drawing to add a layout.").as_ref());
return Task::none(); return Task::none();
} }
let existing = self.tabs[i].scene.layout_names(); let existing = self.tabs[i].scene.layout_names();
@ -4134,9 +4134,9 @@ impl OpenCADStudio {
self.layout_manager_selected = name.clone(); self.layout_manager_selected = name.clone();
self.layout_manager_rename_buf = name.clone(); self.layout_manager_rename_buf = name.clone();
self.command_line self.command_line
.push_output(&format!("Layout '{name}' created.")); .push_output(crate::tf!("Layout '{name}' created.").as_ref());
} }
Err(e) => self.command_line.push_error(&format!("LAYOUT: {e}")), Err(e) => self.command_line.push_error(crate::tf!("LAYOUT: {e}").as_ref()),
} }
Task::none() Task::none()
} }
@ -4145,7 +4145,7 @@ impl OpenCADStudio {
let name = self.layout_manager_selected.clone(); let name = self.layout_manager_selected.clone();
if name == "Model" { if name == "Model" {
self.command_line self.command_line
.push_error("Cannot delete the Model layout."); .push_error(crate::t!("Cannot delete the Model layout.").as_ref());
Task::none() Task::none()
} else { } else {
let deleting_current = self.tabs[i].scene.current_layout == name; let deleting_current = self.tabs[i].scene.current_layout == name;
@ -4165,7 +4165,7 @@ impl OpenCADStudio {
self.layout_manager_selected = "Model".to_string(); self.layout_manager_selected = "Model".to_string();
self.layout_manager_rename_buf = String::new(); self.layout_manager_rename_buf = String::new();
self.command_line self.command_line
.push_output(&format!("Layout '{name}' deleted.")); .push_output(crate::tf!("Layout '{name}' deleted.").as_ref());
Task::batch([cancel_task, switch_task]) Task::batch([cancel_task, switch_task])
} }
} }
@ -4209,7 +4209,7 @@ impl OpenCADStudio {
let task = self.on_layout_switch(name.clone()); let task = self.on_layout_switch(name.clone());
if self.tabs[self.active_tab].scene.current_layout == name { if self.tabs[self.active_tab].scene.current_layout == name {
self.command_line self.command_line
.push_output(&format!("Switched to layout '{name}'.")); .push_output(crate::tf!("Switched to layout '{name}'.").as_ref());
} }
task task
} }
@ -4273,10 +4273,10 @@ impl OpenCADStudio {
} }
Message::AliasEditorApply => { Message::AliasEditorApply => {
self.apply_alias_editor_rows(); self.apply_alias_editor_rows();
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"{} alias(es) applied.", "{} alias(es) applied.",
self.command_aliases.len() self.command_aliases.len()
)); ).as_ref());
Task::none() Task::none()
} }
@ -4334,7 +4334,7 @@ impl OpenCADStudio {
} }
Err(error) => self Err(error) => self
.command_line .command_line
.push_error(&format!("Unable to change UI language: {error}")), .push_error(crate::tf!("Unable to change UI language: {error}").as_ref()),
} }
Task::none() Task::none()
} }
@ -4959,7 +4959,7 @@ impl OpenCADStudio {
let phase = Instant::now(); let phase = Instant::now();
self.refresh_properties(); self.refresh_properties();
let properties_ms = phase.elapsed().as_secs_f64() * 1000.0; let properties_ms = phase.elapsed().as_secs_f64() * 1000.0;
self.command_line.push_output("MSPACE"); self.command_line.push_output(crate::t!("MSPACE").as_ref());
if perf { if perf {
crate::perf_record!( crate::perf_record!(
"[perf] viewport-enter total={:.2}ms normalize={:.2}ms display={:.2}ms ucs={:.2}ms properties={:.2}ms handle={}", "[perf] viewport-enter total={:.2}ms normalize={:.2}ms display={:.2}ms ucs={:.2}ms properties={:.2}ms handle={}",
@ -4996,7 +4996,7 @@ impl OpenCADStudio {
// Paper space has no UCS — drop the viewport's UCS. // Paper space has no UCS — drop the viewport's UCS.
self.tabs[i].refresh_active_ucs(); self.tabs[i].refresh_active_ucs();
self.refresh_properties(); self.refresh_properties();
self.command_line.push_output("PSPACE"); self.command_line.push_output(crate::t!("PSPACE").as_ref());
if context_changed { if context_changed {
self.sync_dyn_fields(); self.sync_dyn_fields();
} }
@ -5007,7 +5007,7 @@ impl OpenCADStudio {
let i = self.active_tab; let i = self.active_tab;
if self.tabs[i].scene.current_layout == "Model" { if self.tabs[i].scene.current_layout == "Model" {
self.command_line self.command_line
.push_error("MS is only available in paper space layouts."); .push_error(crate::t!("MS is only available in paper space layouts.").as_ref());
return Task::none(); return Task::none();
} }
if self.tabs[i].scene.active_viewport.is_some() { if self.tabs[i].scene.active_viewport.is_some() {
@ -5018,7 +5018,7 @@ impl OpenCADStudio {
Some(handle) => Task::done(Message::EnterViewport(handle)), Some(handle) => Task::done(Message::EnterViewport(handle)),
None => { None => {
self.command_line self.command_line
.push_error("No viewport found in this layout."); .push_error(crate::t!("No viewport found in this layout.").as_ref());
Task::none() Task::none()
} }
} }
@ -5163,7 +5163,7 @@ impl OpenCADStudio {
Ok(msg) => self.command_line.push_info(&msg), Ok(msg) => self.command_line.push_info(&msg),
Err(err) => self Err(err) => self
.command_line .command_line
.push_error(&format!("Could not set default app: {err}")), .push_error(crate::tf!("Could not set default app: {err}").as_ref()),
} }
Task::none() Task::none()
} }
@ -5252,11 +5252,11 @@ impl OpenCADStudio {
Message::PrintToPrinter => self.on_print_to_printer(), Message::PrintToPrinter => self.on_print_to_printer(),
Message::PrintResult(Ok(printer)) => { Message::PrintResult(Ok(printer)) => {
self.command_line self.command_line
.push_info(&format!("Sent to printer: {printer}")); .push_info(crate::tf!("Sent to printer: {printer}").as_ref());
Task::none() Task::none()
} }
Message::PrintResult(Err(e)) => { Message::PrintResult(Err(e)) => {
self.command_line.push_error(&format!("Print failed: {e}")); self.command_line.push_error(crate::tf!("Print failed: {e}").as_ref());
Task::none() Task::none()
} }
@ -5273,7 +5273,7 @@ impl OpenCADStudio {
} }
self.plot_dialog.style_name = table.name.clone(); self.plot_dialog.style_name = table.name.clone();
self.plot_dialog.style_missing = false; self.plot_dialog.style_missing = false;
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Plot style '{}' loaded ({} color entries).", "Plot style '{}' loaded ({} color entries).",
table.name, table.name,
table table
@ -5281,7 +5281,7 @@ impl OpenCADStudio {
.iter() .iter()
.filter(|e| e.color.is_some()) .filter(|e| e.color.is_some())
.count() .count()
)); ).as_ref());
self.active_plot_style = Some(table); self.active_plot_style = Some(table);
self.plot_dialog.plot_styles = crate::io::plot_style::available_ctb_names(); self.plot_dialog.plot_styles = crate::io::plot_style::available_ctb_names();
Task::none() Task::none()
@ -5291,7 +5291,7 @@ impl OpenCADStudio {
self.active_plot_style = None; self.active_plot_style = None;
self.plot_dialog.style_name.clear(); self.plot_dialog.style_name.clear();
self.plot_dialog.style_missing = false; self.plot_dialog.style_missing = false;
self.command_line.push_output("Plot style table cleared."); self.command_line.push_output(crate::t!("Plot style table cleared.").as_ref());
Task::none() Task::none()
} }
@ -5383,12 +5383,12 @@ impl OpenCADStudio {
self.plot_dialog.style_missing = false; self.plot_dialog.style_missing = false;
self.plot_dialog.plot_styles = self.plot_dialog.plot_styles =
crate::io::plot_style::available_ctb_names(); crate::io::plot_style::available_ctb_names();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Plot style table saved to \"{}\".", "Plot style table saved to \"{}\".",
path.display() path.display()
)); ).as_ref());
} }
Err(e) => self.command_line.push_error(&format!("Save error: {e}")), Err(e) => self.command_line.push_error(crate::tf!("Save error: {e}").as_ref()),
} }
} }
Task::none() Task::none()
@ -5416,7 +5416,7 @@ impl OpenCADStudio {
self.tabs[i].scene.document.header.current_text_style_name = name.clone(); self.tabs[i].scene.document.header.current_text_style_name = name.clone();
self.sync_ribbon_styles(); self.sync_ribbon_styles();
self.command_line self.command_line
.push_output(&format!("Current text style: {}", name)); .push_output(crate::tf!("Current text style: {}", name).as_ref());
} }
Task::none() Task::none()
} }
@ -5716,7 +5716,7 @@ impl OpenCADStudio {
self.tabs[i].scene.document.header.current_table_style_name = name.clone(); self.tabs[i].scene.document.header.current_table_style_name = name.clone();
self.ribbon.active_table_style = name.clone(); self.ribbon.active_table_style = name.clone();
self.command_line self.command_line
.push_output(&format!("Current table style: {name}")); .push_output(crate::tf!("Current table style: {name}").as_ref());
} }
Task::none() Task::none()
} }
@ -5745,7 +5745,7 @@ impl OpenCADStudio {
// Staged: persists on Apply. // Staged: persists on Apply.
self.tabs[i].scene.document.header.multiline_style = name.clone(); self.tabs[i].scene.document.header.multiline_style = name.clone();
self.command_line self.command_line
.push_output(&format!("Current multiline style: {}", name)); .push_output(crate::tf!("Current multiline style: {}", name).as_ref());
} }
Task::none() Task::none()
} }
@ -5878,10 +5878,10 @@ impl OpenCADStudio {
self.tabs[i].scene.document.header.current_dimstyle_name = self.tabs[i].scene.document.header.current_dimstyle_name =
self.dimstyle_selected.clone(); self.dimstyle_selected.clone();
self.sync_ribbon_styles(); self.sync_ribbon_styles();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Current dim style set to '{}'.", "Current dim style set to '{}'.",
self.dimstyle_selected self.dimstyle_selected
)); ).as_ref());
Task::none() Task::none()
} }
Message::DimStyleDialogDelete => { Message::DimStyleDialogDelete => {

View file

@ -368,7 +368,7 @@ impl OpenCADStudio {
ds.dimalt = self.ds_dimalt; ds.dimalt = self.ds_dimalt;
ds.dimapost = self.ds_dimapost.clone(); ds.dimapost = self.ds_dimapost.clone();
self.command_line self.command_line
.push_output(&format!("DimStyle '{}' updated.", self.dimstyle_selected)); .push_output(crate::tf!("DimStyle '{}' updated.", self.dimstyle_selected).as_ref());
} }
/// Update a single string buffer field. /// Update a single string buffer field.
@ -687,7 +687,7 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
self.tabs[i].active_mleader_style = name.clone(); self.tabs[i].active_mleader_style = name.clone();
self.ribbon.active_mleader_style = name.clone(); self.ribbon.active_mleader_style = name.clone();
self.command_line self.command_line
.push_output(&format!("Current multileader style: {}", name)); .push_output(crate::tf!("Current multileader style: {}", name).as_ref());
} }
Task::none() Task::none()
} }

View file

@ -515,7 +515,7 @@ impl OpenCADStudio {
if self.tabs[i].scene.set_active_viewport_render_mode(mode) { if self.tabs[i].scene.set_active_viewport_render_mode(mode) {
self.tabs[i].scene.bump_geometry_no_blocks(); self.tabs[i].scene.bump_geometry_no_blocks();
self.command_line self.command_line
.push_output(&format!("Viewport visual style: {label}")); .push_output(crate::tf!("Viewport visual style: {label}").as_ref());
return Task::none(); return Task::none();
} }
self.tabs[i].render_mode = mode; self.tabs[i].render_mode = mode;
@ -533,7 +533,7 @@ impl OpenCADStudio {
// pipeline keys its upload cache off `geometry_epoch`. // pipeline keys its upload cache off `geometry_epoch`.
self.tabs[i].scene.bump_geometry_no_blocks(); self.tabs[i].scene.bump_geometry_no_blocks();
self.command_line self.command_line
.push_output(&format!("Visual style: {label}")); .push_output(crate::tf!("Visual style: {label}").as_ref());
Task::none() Task::none()
} }
@ -2616,7 +2616,7 @@ impl OpenCADStudio {
.as_mut() .as_mut()
.map(|c| c.on_structure_pick(pick.handle, center)); .map(|c| c.on_structure_pick(pick.handle, center));
self.command_line self.command_line
.push_info(&format!("{} acquired.", pick.label)); .push_info(crate::tf!("{} acquired.", pick.label).as_ref());
result result
} else { } else {
let msg = self.tabs[i] let msg = self.tabs[i]
@ -2745,7 +2745,7 @@ impl OpenCADStudio {
self.tabs[i].active_cmd = Some(cmd); self.tabs[i].active_cmd = Some(cmd);
} else { } else {
self.command_line self.command_line
.push_error("HATCHEDIT: not a hatch entity."); .push_error(crate::t!("HATCHEDIT: not a hatch entity.").as_ref());
self.tabs[i].active_cmd = None; self.tabs[i].active_cmd = None;
} }
} }
@ -2772,7 +2772,7 @@ impl OpenCADStudio {
} }
result result
} else { } else {
self.command_line.push_info("Nothing found at that point."); self.command_line.push_info(crate::t!("Nothing found at that point.").as_ref());
None None
} }
} else if self.tabs[i] } else if self.tabs[i]
@ -2787,7 +2787,7 @@ impl OpenCADStudio {
.as_mut() .as_mut()
.map(|c| c.on_tangent_point(obj, pick_wcs)) .map(|c| c.on_tangent_point(obj, pick_wcs))
} else { } else {
self.command_line.push_info("Select a tangent object."); self.command_line.push_info(crate::t!("Select a tangent object.").as_ref());
None None
} }
} else if !self.command_point_allowed(i, world_pt) { } else if !self.command_point_allowed(i, world_pt) {
@ -3250,9 +3250,9 @@ impl OpenCADStudio {
// `selection_just_completed`, or a // `selection_just_completed`, or a
// gather command (MOVE's "select // gather command (MOVE's "select
// objects") would wrongly finish. // objects") would wrongly finish.
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"Object is on locked layer \"{layer}\" — unlock the layer to select or edit it." "Object is on locked layer \"{layer}\" — unlock the layer to select or edit it."
)); ).as_ref());
} else { } else {
// Individual picks accumulate (issue #47): // Individual picks accumulate (issue #47):
// each plain click adds to the selection, // each plain click adds to the selection,
@ -3513,9 +3513,9 @@ impl OpenCADStudio {
// Locked layer: double-click must not open any editor // Locked layer: double-click must not open any editor
// (text / attribute / in-place block edit). // (text / attribute / in-place block edit).
if let Some(layer) = self.tabs[i].scene.locked_layer_name(handle) { if let Some(layer) = self.tabs[i].scene.locked_layer_name(handle) {
self.command_line.push_info(&format!( self.command_line.push_info(crate::tf!(
"Object is on locked layer \"{layer}\" — unlock the layer to edit it." "Object is on locked layer \"{layer}\" — unlock the layer to edit it."
)); ).as_ref());
return Task::none(); return Task::none();
} }
// Any text-bearing entity opens its in-place editor // Any text-bearing entity opens its in-place editor
@ -3693,7 +3693,7 @@ impl OpenCADStudio {
self.tabs[i] self.tabs[i]
.scene .scene
.record_nav_perf(crate::scene::NavPerfOp::Zoom, now); .record_nav_perf(crate::scene::NavPerfOp::Zoom, now);
self.command_line.push_output("Zoom Extents"); self.command_line.push_output(crate::t!("Zoom Extents").as_ref());
} }
Task::none() Task::none()
} }
@ -3891,7 +3891,7 @@ impl OpenCADStudio {
} }
self.tabs[i].scene.camera_generation += 1; self.tabs[i].scene.camera_generation += 1;
self.command_line self.command_line
.push_output(&format!("View: {}", region.label())); .push_output(crate::tf!("View: {}", region.label()).as_ref());
Task::none() Task::none()
} }
@ -4094,7 +4094,7 @@ impl OpenCADStudio {
.position(|session| session.block_name == name) .position(|session| session.block_name == name)
else { else {
self.command_line self.command_line
.push_error(&format!("BEDIT: block tab \"{name}\" is not open.")); .push_error(crate::tf!("BEDIT: block tab \"{name}\" is not open.").as_ref());
return Task::none(); return Task::none();
}; };
if self.tabs[i].active_block_edit == Some(target_index) { if self.tabs[i].active_block_edit == Some(target_index) {
@ -4156,7 +4156,7 @@ impl OpenCADStudio {
let i = self.active_tab; let i = self.active_tab;
if self.tabs[i].is_start { if self.tabs[i].is_start {
self.command_line self.command_line
.push_info("Open or create a drawing to switch layouts."); .push_info(crate::t!("Open or create a drawing to switch layouts.").as_ref());
return Task::none(); return Task::none();
} }
let perf = crate::perf::enabled(); let perf = crate::perf::enabled();
@ -4270,7 +4270,7 @@ impl OpenCADStudio {
let i = self.active_tab; let i = self.active_tab;
if self.tabs[i].is_start { if self.tabs[i].is_start {
self.command_line self.command_line
.push_info("Open or create a drawing to add a layout."); .push_info(crate::t!("Open or create a drawing to add a layout.").as_ref());
return Task::none(); return Task::none();
} }
let cancel_task = self.cancel_active_command_for_space_change(); let cancel_task = self.cancel_active_command_for_space_change();
@ -4310,15 +4310,15 @@ impl OpenCADStudio {
self.tabs[i].scene.ensure_sheet_viewport(&new_name); self.tabs[i].scene.ensure_sheet_viewport(&new_name);
let switch_task = self.on_layout_switch(new_name.clone()); let switch_task = self.on_layout_switch(new_name.clone());
self.tabs[i].scene.fit_all(); self.tabs[i].scene.fit_all();
self.command_line.push_output(&format!( self.command_line.push_output(crate::tf!(
"Layout \"{new_name}\" created — use MVIEW to add a viewport" "Layout \"{new_name}\" created — use MVIEW to add a viewport"
)); ).as_ref());
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
return Task::batch([cancel_task, switch_task]); return Task::batch([cancel_task, switch_task]);
} }
Err(e) => self Err(e) => self
.command_line .command_line
.push_error(&format!("Failed to create layout: {e}")), .push_error(crate::tf!("Failed to create layout: {e}").as_ref()),
} }
cancel_task cancel_task
} }
@ -4342,7 +4342,7 @@ impl OpenCADStudio {
.is_some() .is_some()
{ {
self.command_line self.command_line
.push_error(&format!("\"{}\" name already in use", new_name)); .push_error(crate::tf!("\"{}\" name already in use", new_name).as_ref());
} else { } else {
self.push_undo_snapshot(i, "BLOCK RENAME"); self.push_undo_snapshot(i, "BLOCK RENAME");
if self.tabs[i].scene.rename_block(&orig, &new_name) { if self.tabs[i].scene.rename_block(&orig, &new_name) {
@ -4358,10 +4358,10 @@ impl OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("Block \"{orig}\" → \"{new_name}\"")); .push_output(crate::tf!("Block \"{orig}\" → \"{new_name}\"").as_ref());
} else { } else {
self.command_line self.command_line
.push_error(&format!("Could not rename block \"{orig}\"")); .push_error(crate::tf!("Could not rename block \"{orig}\"").as_ref());
} }
} }
return Task::none(); return Task::none();
@ -4373,7 +4373,7 @@ impl OpenCADStudio {
.any(|n| *n == new_name); .any(|n| *n == new_name);
if exists { if exists {
self.command_line self.command_line
.push_error(&format!("\"{}\" name already in use", new_name)); .push_error(crate::tf!("\"{}\" name already in use", new_name).as_ref());
} else { } else {
self.push_undo_snapshot(i, "LAYOUT RENAME"); self.push_undo_snapshot(i, "LAYOUT RENAME");
self.tabs[i].scene.rename_layout(&orig, &new_name); self.tabs[i].scene.rename_layout(&orig, &new_name);
@ -4382,7 +4382,7 @@ impl OpenCADStudio {
} }
self.tabs[i].dirty = true; self.tabs[i].dirty = true;
self.command_line self.command_line
.push_output(&format!("Layout \"{orig}\" → \"{new_name}\"")); .push_output(crate::tf!("Layout \"{orig}\" → \"{new_name}\"").as_ref());
} }
} }
} }

View file

@ -14,6 +14,7 @@ use iced::widget::{
use iced::window; use iced::window;
use iced::{keyboard, Background, Border, Color, Element, Fill, Subscription, Task, Theme}; use iced::{keyboard, Background, Border, Color, Element, Fill, Subscription, Task, Theme};
use iced_aw::ContextMenu; use iced_aw::ContextMenu;
use crate::t;
mod controls; mod controls;
mod modal; mod modal;
@ -1210,7 +1211,7 @@ impl OpenCADStudio {
let copy_btn = button( let copy_btn = button(
row![ row![
crate::ui::icons::themed_primary(crate::ui::icons::COPY, 11.0), crate::ui::icons::themed_primary(crate::ui::icons::COPY, 11.0),
text("Copy").size(11), text(t!("Copy")).size(11),
] ]
.spacing(4) .spacing(4)
.align_y(iced::Center), .align_y(iced::Center),
@ -1221,7 +1222,7 @@ impl OpenCADStudio {
let clear_btn = button( let clear_btn = button(
row![ row![
crate::ui::icons::themed_danger(crate::ui::icons::TRASH, 11.0), crate::ui::icons::themed_danger(crate::ui::icons::TRASH, 11.0),
text("Clear").size(11), text(t!("Clear")).size(11),
] ]
.spacing(4) .spacing(4)
.align_y(iced::Center), .align_y(iced::Center),
@ -1230,7 +1231,7 @@ impl OpenCADStudio {
.style(perf_button_style) .style(perf_button_style)
.padding([2, 6]); .padding([2, 6]);
let header = row![ let header = row![
text("PERF").size(12).style(|theme: &Theme| iced::widget::text::Style { text(t!("PERF")).size(12).style(|theme: &Theme| iced::widget::text::Style {
color: Some(theme.palette().success.base.color), color: Some(theme.palette().success.base.color),
}), }),
Space::new().width(iced::Length::Fill), Space::new().width(iced::Length::Fill),

View file

@ -1,6 +1,8 @@
use super::super::{Message, OpenCADStudio}; use super::super::{Message, OpenCADStudio};
use crate::t;
use iced::widget::{button, column, container, row, text, Space}; use iced::widget::{button, column, container, row, text, Space};
use iced::{Background, Element, Fill, Fit, Theme}; use iced::{Background, Element, Fill, Fit, Theme};
use std::borrow::Cow;
impl OpenCADStudio { impl OpenCADStudio {
/// Title shown in the active modal's title bar. Keep in sync with the /// Title shown in the active modal's title bar. Keep in sync with the
@ -221,7 +223,7 @@ impl OpenCADStudio {
) )
} else { } else {
automatic_flow(ex, |flow| { automatic_flow(ex, |flow| {
container(text("The selected layer state is no longer available.")) container(text(t!("The selected layer state is no longer available.")))
.padding(16) .padding(16)
.width(flow.width) .width(flow.width)
.height(flow.height) .height(flow.height)
@ -941,12 +943,12 @@ fn automatic_flow<'a>(
) )
} }
fn dialog_button( fn dialog_button<'a>(
label: &'static str, label: impl Into<Cow<'a, str>>,
message: Message, message: Message,
style: fn(&Theme, button::Status) -> button::Style, style: fn(&Theme, button::Status) -> button::Style,
) -> Element<'static, Message> { ) -> Element<'a, Message> {
button(text(label).size(13)) button(text(label.into()).size(13))
.on_press(message) .on_press(message)
.style(style) .style(style)
.padding([6, 18]) .padding([6, 18])
@ -980,7 +982,7 @@ fn save_as_dialog_window<'a>(
.iter() .iter()
.copied() .copied()
.find(|&s| s == format); .find(|&s| s == format);
let label = |s: &'static str| text(s).size(11).style(dialog_muted_text_style); let label = |s: Cow<'static, str>| text(s).size(11).style(dialog_muted_text_style);
let field_width = if matches!(sizing.width, iced::Length::Fill) { let field_width = if matches!(sizing.width, iced::Length::Fill) {
Fill Fill
} else { } else {
@ -988,7 +990,7 @@ fn save_as_dialog_window<'a>(
}; };
let mut items: Vec<Element<'a, Message>> = Vec::new(); let mut items: Vec<Element<'a, Message>> = Vec::new();
items.push(text("Save Drawing As").size(14).into()); items.push(text(t!("Save Drawing As")).size(14).into());
items.push(Space::new().height(12).into()); items.push(Space::new().height(12).into());
// Web has no native file dialog, so the file name is typed here. On native // Web has no native file dialog, so the file name is typed here. On native
@ -997,7 +999,7 @@ fn save_as_dialog_window<'a>(
{ {
items.push( items.push(
row![ row![
label("File name:").width(70), label(t!("File name:")).width(70),
iced::widget::text_input("drawing.dwg", filename) iced::widget::text_input("drawing.dwg", filename)
.on_input(Message::SaveDialogFilenameChanged) .on_input(Message::SaveDialogFilenameChanged)
.size(13) .size(13)
@ -1016,7 +1018,7 @@ fn save_as_dialog_window<'a>(
items.push( items.push(
row![ row![
label("Format:").width(70), label(t!("Format:")).width(70),
iced::widget::pick_list( iced::widget::pick_list(
sel_fmt, sel_fmt,
crate::io::SAVE_FORMAT_OPTIONS, crate::io::SAVE_FORMAT_OPTIONS,
@ -1034,9 +1036,9 @@ fn save_as_dialog_window<'a>(
items.push( items.push(
row![ row![
Space::new().width(Fit), Space::new().width(Fit),
dialog_button("Save as...", Message::SaveDialogConfirm, button::primary), dialog_button(t!("Save as..."), Message::SaveDialogConfirm, button::primary),
Space::new().width(8), Space::new().width(8),
dialog_button("Cancel", Message::SaveDialogCancel, button::secondary), dialog_button(t!("Cancel"), Message::SaveDialogCancel, button::secondary),
] ]
.into(), .into(),
); );
@ -1058,18 +1060,21 @@ fn unsaved_changes_dialog_window(
name: &str, name: &str,
sizing: crate::ui::modal::ModalSizing, sizing: crate::ui::modal::ModalSizing,
) -> Element<'static, Message> { ) -> Element<'static, Message> {
let body_text = format!("Do you want to save changes to \"{}\"?", name); let body_text = t!(
"Do you want to save changes to \"%{name}\"?",
name = name
);
container( container(
column![ column![
text(body_text).size(13), text(body_text).size(13),
iced::widget::Space::new().height(20), iced::widget::Space::new().height(20),
row![ row![
dialog_button("Save", Message::UnsavedDialogSave, button::primary), dialog_button(t!("Save"), Message::UnsavedDialogSave, button::primary),
iced::widget::Space::new().width(8), iced::widget::Space::new().width(8),
dialog_button("Discard", Message::UnsavedDialogDiscard, button::danger), dialog_button(t!("Discard"), Message::UnsavedDialogDiscard, button::danger),
iced::widget::Space::new().width(8), iced::widget::Space::new().width(8),
dialog_button("Cancel", Message::UnsavedDialogCancel, button::secondary), dialog_button(t!("Cancel"), Message::UnsavedDialogCancel, button::secondary),
], ],
] ]
.spacing(0), .spacing(0),
@ -1090,19 +1095,18 @@ fn file_in_use_dialog_window(
let file_name = std::path::Path::new(path) let file_name = std::path::Path::new(path)
.file_name() .file_name()
.map(|name| name.to_string_lossy().into_owned()) .map(|name| name.to_string_lossy().into_owned())
.unwrap_or_else(|| "Drawing".to_string()); .unwrap_or_else(|| t!("Drawing").into_owned());
let heading = format!("\"{file_name}\" could not be saved."); let heading = t!("\"%{file_name}\" could not be saved.", file_name = file_name);
let path_line = format!("Path: {path}"); let path_line = t!("Path: %{path}", path = path);
let details = format!("Details: {error}"); let details = t!("Details: %{error}", error = error);
container( container(
column![ column![
text(heading).size(14), text(heading).size(14),
Space::new().height(8), Space::new().height(8),
text( text(t!(
"The file is open or being used by another application. \ "The file is open or being used by another application. Close it there and retry, or save this drawing under a different name."
Close it there and retry, or save this drawing under a different name." ))
)
.size(13) .size(13)
.width(Fit), .width(Fit),
Space::new().height(12), Space::new().height(12),
@ -1112,19 +1116,19 @@ fn file_in_use_dialog_window(
Space::new().height(18), Space::new().height(18),
row![ row![
dialog_button( dialog_button(
"Retry", t!("Retry"),
Message::SaveFileInUseRetry, Message::SaveFileInUseRetry,
button::primary button::primary
), ),
Space::new().width(8), Space::new().width(8),
dialog_button( dialog_button(
"Save As", t!("Save As"),
Message::SaveFileInUseSaveAs, Message::SaveFileInUseSaveAs,
button::secondary button::secondary
), ),
Space::new().width(8), Space::new().width(8),
dialog_button( dialog_button(
"Cancel", t!("Cancel"),
Message::SaveFileInUseCancel, Message::SaveFileInUseCancel,
button::secondary button::secondary
), ),
@ -1147,18 +1151,17 @@ fn external_change_dialog_window(
let file_name = std::path::Path::new(path) let file_name = std::path::Path::new(path)
.file_name() .file_name()
.map(|name| name.to_string_lossy().into_owned()) .map(|name| name.to_string_lossy().into_owned())
.unwrap_or_else(|| "Drawing".to_string()); .unwrap_or_else(|| t!("Drawing").into_owned());
let heading = format!("\"{file_name}\" was changed by another application."); let heading = t!("\"%{file_name}\" was changed by another application.", file_name = file_name);
let path_line = format!("Path: {path}"); let path_line = t!("Path: %{path}", path = path);
container( container(
column![ column![
text(heading).size(14), text(heading).size(14),
Space::new().height(8), Space::new().height(8),
text( text(t!(
"Saving now could destroy those external changes. Reload the disk copy, \ "Saving now could destroy those external changes. Reload the disk copy, save your local work elsewhere, or explicitly overwrite it."
save your local work elsewhere, or explicitly overwrite it." ))
)
.size(13) .size(13)
.width(Fit), .width(Fit),
Space::new().height(12), Space::new().height(12),
@ -1166,25 +1169,25 @@ fn external_change_dialog_window(
Space::new().height(18), Space::new().height(18),
row![ row![
dialog_button( dialog_button(
"Reload from Disk", t!("Reload from Disk"),
Message::ExternalChangeReload, Message::ExternalChangeReload,
button::primary button::primary
), ),
Space::new().width(8), Space::new().width(8),
dialog_button( dialog_button(
"Save As", t!("Save As"),
Message::ExternalChangeSaveAs, Message::ExternalChangeSaveAs,
button::secondary button::secondary
), ),
Space::new().width(8), Space::new().width(8),
dialog_button( dialog_button(
"Overwrite", t!("Overwrite"),
Message::ExternalChangeOverwrite, Message::ExternalChangeOverwrite,
button::danger button::danger
), ),
Space::new().width(8), Space::new().width(8),
dialog_button( dialog_button(
"Cancel", t!("Cancel"),
Message::ExternalChangeCancel, Message::ExternalChangeCancel,
button::secondary button::secondary
), ),
@ -1209,10 +1212,11 @@ fn aec_drop_dialog_window(
src_version: &str, src_version: &str,
sizing: crate::ui::modal::ModalSizing, sizing: crate::ui::modal::ModalSizing,
) -> Element<'static, Message> { ) -> Element<'static, Message> {
let body_text = format!( let body_text = t!(
"This drawing contains {count} AEC/Civil objects that \"{target}\" \ "This drawing contains %{count} AEC/Civil objects that \"%{target}\" cannot store, so they will not be saved.\n\nTo keep them, save in the source version (%{src_version}).",
cannot store, so they will not be saved.\n\n\ count = count,
To keep them, save in the source version ({src_version})." target = target,
src_version = src_version,
); );
container( container(
@ -1221,14 +1225,14 @@ fn aec_drop_dialog_window(
iced::widget::Space::new().height(20), iced::widget::Space::new().height(20),
row![ row![
dialog_button( dialog_button(
"Save in source version", t!("Save in source version"),
Message::AecDropSameVersion, Message::AecDropSameVersion,
button::primary button::primary
), ),
iced::widget::Space::new().width(8), iced::widget::Space::new().width(8),
dialog_button("Save anyway", Message::AecDropProceed, button::warning), dialog_button(t!("Save anyway"), Message::AecDropProceed, button::warning),
iced::widget::Space::new().width(8), iced::widget::Space::new().width(8),
dialog_button("Back", Message::AecDropBack, button::secondary), dialog_button(t!("Back"), Message::AecDropBack, button::secondary),
], ],
] ]
.spacing(0), .spacing(0),
@ -1249,16 +1253,25 @@ fn layer_delete_warning_window(
count: usize, count: usize,
sizing: crate::ui::modal::ModalSizing, sizing: crate::ui::modal::ModalSizing,
) -> Element<'static, Message> { ) -> Element<'static, Message> {
let obj = if count == 1 { "object" } else { "objects" }; let obj = if count == 1 { t!("object") } else { t!("objects") };
let subject = if names.len() == 1 { let has = if names.len() == 1 { t!("has") } else { t!("hold") };
format!("Layer \"{}\"", names[0]) let those = if count == 1 {
t!("that object")
} else { } else {
format!("{} selected layers", names.len()) t!("those objects")
}; };
let body_text = format!( let subject = if names.len() == 1 {
"{subject} still {} {count} {obj}.\n\nDeleting will also remove {} from the drawing. Continue?", t!("Layer \"%{name}\"", name = names[0]).into_owned()
if names.len() == 1 { "has" } else { "hold" }, } else {
if count == 1 { "that object" } else { "those objects" } t!("%{count} selected layers", count = names.len()).into_owned()
};
let body_text = t!(
"%{subject} still %{has} %{count} %{obj}.\n\nDeleting will also remove %{those} from the drawing. Continue?",
subject = subject,
has = has,
count = count,
obj = obj,
those = those,
); );
container( container(
@ -1267,12 +1280,12 @@ fn layer_delete_warning_window(
iced::widget::Space::new().height(20), iced::widget::Space::new().height(20),
row![ row![
dialog_button( dialog_button(
"Delete Objects", t!("Delete Objects"),
Message::LayerDeleteConfirm, Message::LayerDeleteConfirm,
button::danger button::danger
), ),
iced::widget::Space::new().width(8), iced::widget::Space::new().width(8),
dialog_button("Cancel", Message::CloseModal, button::secondary), dialog_button(t!("Cancel"), Message::CloseModal, button::secondary),
], ],
] ]
.spacing(0), .spacing(0),
@ -1293,19 +1306,19 @@ fn default_assoc_dialog_window(
) -> Element<'static, Message> { ) -> Element<'static, Message> {
container( container(
column![ column![
text("Make Open CAD Studio your default CAD app?") text(t!("Make Open CAD Studio your default CAD app?"))
.size(15), .size(15),
iced::widget::Space::new().height(10), iced::widget::Space::new().height(10),
text("Open .dwg and .dxf drawings in Open CAD Studio by default. You can change this later in your system settings.") text(t!("Open .dwg and .dxf drawings in Open CAD Studio by default. You can change this later in your system settings."))
.size(12) .size(12)
.style(dialog_muted_text_style), .style(dialog_muted_text_style),
iced::widget::Space::new().height(22), iced::widget::Space::new().height(22),
row![ row![
iced::widget::Space::new().width(Fit), iced::widget::Space::new().width(Fit),
dialog_button("Not now", Message::AssocPromptNo, button::secondary), dialog_button(t!("Not now"), Message::AssocPromptNo, button::secondary),
iced::widget::Space::new().width(8), iced::widget::Space::new().width(8),
dialog_button( dialog_button(
"Yes, set as default", t!("Yes, set as default"),
Message::AssocPromptYes, Message::AssocPromptYes,
button::primary button::primary
), ),

View file

@ -4,6 +4,7 @@ use iced::widget::{
Space, Space,
}; };
use iced::{Background, Border, Color, Element, Fill, Theme}; use iced::{Background, Border, Color, Element, Fill, Theme};
use crate::t;
pub(super) fn position_canvas_overlay<'a>( pub(super) fn position_canvas_overlay<'a>(
anchor: iced::Point, anchor: iced::Point,
@ -28,7 +29,7 @@ pub(super) fn text_inline_overlay(
ed: &super::super::text_inline::TextInlineState, ed: &super::super::text_inline::TextInlineState,
canvas: (f32, f32), canvas: (f32, f32),
) -> Element<'_, Message> { ) -> Element<'_, Message> {
let field = text_input("Text", &ed.value) let field = text_input(t!("Text").as_ref(), &ed.value)
.id(iced::widget::Id::new(TEXT_INLINE_ID)) .id(iced::widget::Id::new(TEXT_INLINE_ID))
.on_input(Message::TextInlineInput) .on_input(Message::TextInlineInput)
.on_submit(Message::TextInlineOk) .on_submit(Message::TextInlineOk)
@ -366,7 +367,7 @@ pub(super) fn mtext_editor_overlay<'a>(
crate::ui::modal::modal( crate::ui::modal::modal(
iced::widget::Space::new().width(Fill).height(Fill), iced::widget::Space::new().width(Fill).height(Fill),
"Text Editor", t!("Text Editor"),
content, content,
Message::MTextCancel, Message::MTextCancel,
modal_offset, modal_offset,
@ -577,7 +578,7 @@ fn mtext_editor_content<'a>(
f64::from(writing_area_px / preview_scale).max(slider_min * 2.0); f64::from(writing_area_px / preview_scale).max(slider_min * 2.0);
let width_slider = column![ let width_slider = column![
row![ row![
text(format!("Width: {:.3}", ed.rect_width)).size(11), text(t!("Width: %{value}", value = format!("{:.3}", ed.rect_width))).size(11),
Space::new().width(width), Space::new().width(width),
text(format!("{:.0}%", ed.rect_width / slider_max * 100.0)).size(11), text(format!("{:.0}%", ed.rect_width / slider_max * 100.0)).size(11),
] ]
@ -709,7 +710,7 @@ fn mtext_editor_content<'a>(
let action_bar = container( let action_bar = container(
row![ row![
iced::widget::Space::new().width(width), iced::widget::Space::new().width(width),
crate::ui::style::style_manager::tb_button("Apply", Message::MTextApply, true), crate::ui::style::style_manager::tb_button(t!("Apply"), Message::MTextApply, true),
] ]
.align_y(iced::Alignment::Center), .align_y(iced::Alignment::Center),
) )
@ -784,13 +785,13 @@ pub(super) fn viewport_context_menu_overlay(
let mut items: Vec<Element<'static, Message>> = Vec::new(); let mut items: Vec<Element<'static, Message>> = Vec::new();
if has_cmd { if has_cmd {
items.push(item("Cancel".to_string(), Message::CommandEscape)); items.push(item(t!("Cancel").into_owned(), Message::CommandEscape));
items.push(item("Enter".to_string(), Message::CommandFinalize)); items.push(item(t!("Enter").into_owned(), Message::CommandFinalize));
} else { } else {
if !last_cmds.is_empty() { if !last_cmds.is_empty() {
let last = last_cmds[0].clone(); let last = last_cmds[0].clone();
items.push(item( items.push(item(
format!("Repeat {last}"), t!("Repeat %{last}", last = last).into_owned(),
Message::Command(last.to_uppercase()), Message::Command(last.to_uppercase()),
)); ));
if last_cmds.len() > 1 { if last_cmds.len() > 1 {
@ -802,13 +803,13 @@ pub(super) fn viewport_context_menu_overlay(
items.push(sep()); items.push(sep());
} }
if has_selection { if has_selection {
items.push(item("Delete".to_string(), Message::DeleteSelected)); items.push(item(t!("Delete").into_owned(), Message::DeleteSelected));
items.push(item( items.push(item(
"Move".to_string(), t!("Move").into_owned(),
Message::Command("MOVE".to_string()), Message::Command("MOVE".to_string()),
)); ));
items.push(item( items.push(item(
"Copy".to_string(), t!("Copy").into_owned(),
Message::Command("COPY".to_string()), Message::Command("COPY".to_string()),
)); ));
items.push(sep()); items.push(sep());
@ -820,7 +821,7 @@ pub(super) fn viewport_context_menu_overlay(
items.push( items.push(
button( button(
row![ row![
text("Draw Order").size(12), text(t!("Draw Order").into_owned()).size(12),
iced::widget::Space::new().width(Fill), iced::widget::Space::new().width(Fill),
do_caret, do_caret,
] ]
@ -834,51 +835,51 @@ pub(super) fn viewport_context_menu_overlay(
); );
if draworder_open { if draworder_open {
items.push(subitem( items.push(subitem(
"Bring to Front".to_string(), t!("Bring to Front").into_owned(),
Message::Command("DRAWORDER F".to_string()), Message::Command("DRAWORDER F".to_string()),
)); ));
items.push(subitem( items.push(subitem(
"Send to Back".to_string(), t!("Send to Back").into_owned(),
Message::Command("DRAWORDER B".to_string()), Message::Command("DRAWORDER B".to_string()),
)); ));
items.push(subitem( items.push(subitem(
"Bring Above Object".to_string(), t!("Bring Above Object").into_owned(),
Message::DrawOrderPickRef(true), Message::DrawOrderPickRef(true),
)); ));
items.push(subitem( items.push(subitem(
"Send Under Object".to_string(), t!("Send Under Object").into_owned(),
Message::DrawOrderPickRef(false), Message::DrawOrderPickRef(false),
)); ));
} }
items.push(sep()); items.push(sep());
items.push(item( items.push(item(
"Isolate Objects".to_string(), t!("Isolate Objects").into_owned(),
Message::Command("ISOLATEOBJECTS".to_string()), Message::Command("ISOLATEOBJECTS".to_string()),
)); ));
items.push(item( items.push(item(
"Hide Objects".to_string(), t!("Hide Objects").into_owned(),
Message::Command("HIDEOBJECTS".to_string()), Message::Command("HIDEOBJECTS".to_string()),
)); ));
items.push(sep()); items.push(sep());
items.push(item("Select Similar".to_string(), Message::SelectSimilar)); items.push(item(t!("Select Similar").into_owned(), Message::SelectSimilar));
items.push(item( items.push(item(
"Invert Selection".to_string(), t!("Invert Selection").into_owned(),
Message::InvertSelection, Message::InvertSelection,
)); ));
} }
if isolation_active { if isolation_active {
items.push(item( items.push(item(
"End Object Isolation".to_string(), t!("End Object Isolation").into_owned(),
Message::Command("UNISOLATEOBJECTS".to_string()), Message::Command("UNISOLATEOBJECTS".to_string()),
)); ));
} }
items.push(item( items.push(item(
"Select All".to_string(), t!("Select All").into_owned(),
Message::Command("SELECTALL".to_string()), Message::Command("SELECTALL".to_string()),
)); ));
items.push(item("Quick Select...".to_string(), Message::QSelectOpen)); items.push(item(t!("Quick Select...").into_owned(), Message::QSelectOpen));
items.push(item( items.push(item(
"Zoom Extents".to_string(), t!("Zoom Extents").into_owned(),
Message::Command("ZOOM EXTENTS".to_string()), Message::Command("ZOOM EXTENTS".to_string()),
)); ));
} }
@ -1020,7 +1021,7 @@ pub(super) fn qselect_overlay<'a>(
crate::ui::modal::modal( crate::ui::modal::modal(
Space::new().width(Fill).height(Fill), Space::new().width(Fill).height(Fill),
"Quick Select", t!("Quick Select"),
content, content,
Message::QSelectClose, Message::QSelectClose,
modal_offset, modal_offset,
@ -1081,13 +1082,13 @@ fn qselect_content<'a>(
iced::Length::Shrink iced::Length::Shrink
}; };
let label = |s: &'static str| { let label = |s: std::borrow::Cow<'static, str>| {
text(s) text(s)
.size(12) .size(12)
.width(iced::Length::Fixed(90.0)) .width(iced::Length::Fixed(90.0))
}; };
let btn = |lbl: &'static str, msg: Message, primary: bool| { let btn = |lbl: std::borrow::Cow<'static, str>, msg: Message, primary: bool| {
button(text(lbl).size(12)) button(text(lbl).size(12))
.on_press(msg) .on_press(msg)
.style(move |theme: &Theme, st| { .style(move |theme: &Theme, st| {
@ -1124,7 +1125,7 @@ fn qselect_content<'a>(
let panel_body = column![ let panel_body = column![
row![ row![
label("Object type:"), label(t!("Object type:")),
iced::widget::pick_list( iced::widget::pick_list(
Some(type_sel), Some(type_sel),
type_options, type_options,
@ -1144,7 +1145,7 @@ fn qselect_content<'a>(
.width(sizing.width), .width(sizing.width),
Space::new().height(6), Space::new().height(6),
row![ row![
label("Property:"), label(t!("Property:")),
iced::widget::pick_list( iced::widget::pick_list(
Some(prop_sel), Some(prop_sel),
prop_options, prop_options,
@ -1164,7 +1165,7 @@ fn qselect_content<'a>(
.width(sizing.width), .width(sizing.width),
Space::new().height(6), Space::new().height(6),
row![ row![
label("Operator:"), label(t!("Operator:")),
iced::widget::pick_list( iced::widget::pick_list(
Some(state.operator), Some(state.operator),
op_options, op_options,
@ -1177,7 +1178,7 @@ fn qselect_content<'a>(
.spacing(8) .spacing(8)
.width(sizing.width), .width(sizing.width),
Space::new().height(6), Space::new().height(6),
row![label("Value:"), value_input,] row![label(t!("Value:")), value_input,]
.align_y(iced::Alignment::Center) .align_y(iced::Alignment::Center)
.spacing(8) .spacing(8)
.width(sizing.width), .width(sizing.width),
@ -1187,15 +1188,15 @@ fn qselect_content<'a>(
.on_toggle(Message::QSelectSetAppend) .on_toggle(Message::QSelectSetAppend)
.size(14), .size(14),
Space::new().width(6), Space::new().width(6),
text("Append to current selection").size(12), text(t!("Append to current selection")).size(12),
] ]
.align_y(iced::Alignment::Center), .align_y(iced::Alignment::Center),
Space::new().height(14), Space::new().height(14),
row![ row![
Space::new().width(field_width), Space::new().width(field_width),
btn("Cancel", Message::QSelectClose, false), btn(t!("Cancel"), Message::QSelectClose, false),
Space::new().width(8), Space::new().width(8),
btn("Apply", Message::QSelectApply, true), btn(t!("Apply"), Message::QSelectApply, true),
] ]
.align_y(iced::Alignment::Center), .align_y(iced::Alignment::Center),
] ]

View file

@ -106,7 +106,7 @@ impl CadCommand for ValuePromptCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
self.prompt.to_string() crate::t!(self.prompt).into_owned()
} }
fn wants_text_input(&self) -> bool { fn wants_text_input(&self) -> bool {
@ -177,9 +177,18 @@ impl CadCommand for RenameCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
RenameStep::Type => "RENAME Select the object type to rename:".to_string(), RenameStep::Type => crate::t!("RENAME Select the object type to rename:").into_owned(),
RenameStep::Old { ty } => format!("RENAME {ty} Enter the current name:"), RenameStep::Old { ty } => crate::t!(
RenameStep::New { ty, old } => format!("RENAME {ty} Rename \"{old}\" to:"), "RENAME %{type} Enter the current name:",
type = ty
)
.into_owned(),
RenameStep::New { ty, old } => crate::t!(
"RENAME %{type} Rename \"%{old}\" to:",
type = ty,
old = old
)
.into_owned(),
} }
} }
@ -274,8 +283,10 @@ impl CadCommand for UserRegCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.slot { match self.slot {
None => format!("{} which register? [1-5]:", self.name), None => crate::t!("%{name} which register? [1-5]:", name = self.name)
Some(n) => format!("{}{n} new value:", self.name), .into_owned(),
Some(n) => crate::t!("%{name}%{slot} new value:", name = self.name, slot = n)
.into_owned(),
} }
} }
@ -369,8 +380,8 @@ impl CadCommand for KeywordCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.pending { match self.pending {
Some((_, value_prompt)) => value_prompt.to_string(), Some((_, value_prompt)) => crate::t!(value_prompt).into_owned(),
None => self.prompt.to_string(), None => crate::t!(self.prompt).into_owned(),
} }
} }
@ -464,8 +475,8 @@ impl CadCommand for TwoValuePromptCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.first { match &self.first {
None => self.prompt1.to_string(), None => crate::t!(self.prompt1).into_owned(),
Some(_) => self.prompt2.to_string(), Some(_) => crate::t!(self.prompt2).into_owned(),
} }
} }
@ -548,11 +559,15 @@ impl CadCommand for SelectThenKeywordCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
if self.gathering { if self.gathering {
return format!("{} select objects, then press Enter:", self.name); return crate::t!(
"%{name} select objects, then press Enter:",
name = self.name
)
.into_owned();
} }
match self.pending { match self.pending {
Some((_, value_prompt)) => value_prompt.to_string(), Some((_, value_prompt)) => crate::t!(value_prompt).into_owned(),
None => self.prompt.to_string(), None => crate::t!(self.prompt).into_owned(),
} }
} }
@ -655,9 +670,13 @@ impl CadCommand for SelectThenValueCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
if self.gathering { if self.gathering {
format!("{} select objects, then press Enter:", self.name) crate::t!(
"%{name} select objects, then press Enter:",
name = self.name
)
.into_owned()
} else { } else {
self.value_prompt.to_string() crate::t!(self.value_prompt).into_owned()
} }
} }
@ -740,11 +759,15 @@ impl CadCommand for SelectThenTwoValueCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
if self.gathering { if self.gathering {
format!("{} select objects, then press Enter:", self.name) crate::t!(
"%{name} select objects, then press Enter:",
name = self.name
)
.into_owned()
} else if self.first.is_none() { } else if self.first.is_none() {
self.prompt1.to_string() crate::t!(self.prompt1).into_owned()
} else { } else {
self.prompt2.to_string() crate::t!(self.prompt2).into_owned()
} }
} }
@ -1220,14 +1243,14 @@ impl CmdOption {
/// Button whose keyword is typed on click, e.g. `("Ttr", "TTR")`. /// Button whose keyword is typed on click, e.g. `("Ttr", "TTR")`.
pub fn new(label: &str, keyword: &str) -> Self { pub fn new(label: &str, keyword: &str) -> Self {
Self { Self {
label: label.to_string(), label: crate::t!(label).into_owned(),
keyword: keyword.to_string(), keyword: keyword.to_string(),
} }
} }
/// A "finish" button that submits the step like Enter. /// A "finish" button that submits the step like Enter.
pub fn enter(label: &str) -> Self { pub fn enter(label: &str) -> Self {
Self { Self {
label: label.to_string(), label: crate::t!(label).into_owned(),
keyword: String::new(), keyword: String::new(),
} }
} }

View file

@ -1,4 +1,5 @@
use acadrust::entities::Arc; use acadrust::entities::Arc;
use crate::t;
use truck_modeling::{builder, Point3}; use truck_modeling::{builder, Point3};
use crate::command::EntityTransform; use crate::command::EntityTransform;
@ -152,26 +153,26 @@ fn properties(arc: &Arc) -> Vec<PropSection> {
let (ex, ey, ez) = arc_pt(ea); let (ex, ey, ez) = arc_pt(ea);
vec![PropSection { vec![PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
ro("Start X", "start_x", format!("{sx:.4}")), ro(t!("Start X").as_ref(), "start_x", format!("{sx:.4}")),
ro("Start Y", "start_y", format!("{sy:.4}")), ro(t!("Start Y").as_ref(), "start_y", format!("{sy:.4}")),
ro("Start Z", "start_z", format!("{sz:.4}")), ro(t!("Start Z").as_ref(), "start_z", format!("{sz:.4}")),
edit("Center X", "center_x", arc.center.x), edit(t!("Center X").as_ref(), "center_x", arc.center.x),
edit("Center Y", "center_y", arc.center.y), edit(t!("Center Y").as_ref(), "center_y", arc.center.y),
edit("Center Z", "center_z", arc.center.z), edit(t!("Center Z").as_ref(), "center_z", arc.center.z),
ro("End X", "end_x", format!("{ex:.4}")), ro(t!("End X").as_ref(), "end_x", format!("{ex:.4}")),
ro("End Y", "end_y", format!("{ey:.4}")), ro(t!("End Y").as_ref(), "end_y", format!("{ey:.4}")),
ro("End Z", "end_z", format!("{ez:.4}")), ro(t!("End Z").as_ref(), "end_z", format!("{ez:.4}")),
edit("Radius", "radius", arc.radius), edit(t!("Radius").as_ref(), "radius", arc.radius),
edit_angle("Start angle", "start_angle", sa.to_degrees()), edit_angle(t!("Start angle").as_ref(), "start_angle", sa.to_degrees()),
edit_angle("End angle", "end_angle", ea.to_degrees()), edit_angle(t!("End angle").as_ref(), "end_angle", ea.to_degrees()),
ro("Total angle", "total_angle", format!("{total_angle:.2}")), ro(t!("Total angle").as_ref(), "total_angle", format!("{total_angle:.2}")),
ro("Arc length", "arc_length", format!("{arc_length:.4}")), ro(t!("Arc length").as_ref(), "arc_length", format!("{arc_length:.4}")),
ro("Area", "area", format!("{area:.4}")), ro(t!("Area").as_ref(), "area", format!("{area:.4}")),
ro("Normal X", "normal_x", format!("{:.4}", arc.normal.x)), ro(t!("Normal X").as_ref(), "normal_x", format!("{:.4}", arc.normal.x)),
ro("Normal Y", "normal_y", format!("{:.4}", arc.normal.y)), ro(t!("Normal Y").as_ref(), "normal_y", format!("{:.4}", arc.normal.y)),
ro("Normal Z", "normal_z", format!("{:.4}", arc.normal.z)), ro(t!("Normal Z").as_ref(), "normal_z", format!("{:.4}", arc.normal.z)),
], ],
}] }]
} }

View file

@ -16,6 +16,7 @@ use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Pr
use crate::scene::model::wire_model::SnapHint; use crate::scene::model::wire_model::SnapHint;
use crate::scene::text::lff; use crate::scene::text::lff;
use crate::scene::view::transform; use crate::scene::view::transform;
use crate::t;
// ── Shared helpers ──────────────────────────────────────────────────────────── // ── Shared helpers ────────────────────────────────────────────────────────────
@ -414,22 +415,22 @@ impl PropertyEditable for AttributeDefinition {
fn geometry_properties(&self, text_style_names: &[String]) -> Vec<PropSection> { fn geometry_properties(&self, text_style_names: &[String]) -> Vec<PropSection> {
let mut text_props = vec![ let mut text_props = vec![
Property { Property {
label: "Tag".into(), label: t!("Tag").into_owned(),
field: "att_tag", field: "att_tag",
value: PropValue::EditText(self.tag.clone()), value: PropValue::EditText(self.tag.clone()),
}, },
Property { Property {
label: "Prompt".into(), label: t!("Prompt").into_owned(),
field: "att_prompt", field: "att_prompt",
value: PropValue::EditText(self.prompt.clone()), value: PropValue::EditText(self.prompt.clone()),
}, },
Property { Property {
label: "Value".into(), label: t!("Value").into_owned(),
field: "att_default", field: "att_default",
value: PropValue::EditText(self.default_value.clone()), value: PropValue::EditText(self.default_value.clone()),
}, },
Property { Property {
label: "Style".into(), label: t!("Style").into_owned(),
field: "att_style", field: "att_style",
value: PropValue::Choice { value: PropValue::Choice {
selected: if self.text_style.trim().is_empty() { selected: if self.text_style.trim().is_empty() {
@ -441,12 +442,12 @@ impl PropertyEditable for AttributeDefinition {
}, },
}, },
ro( ro(
"Annotative", t!("Annotative").as_ref(),
"att_annotative", "att_annotative",
bool_yn(self.flags.annotative), bool_yn(self.flags.annotative),
), ),
Property { Property {
label: "Justify".into(), label: t!("Justify").into_owned(),
field: "att_halign", field: "att_halign",
value: PropValue::Choice { value: PropValue::Choice {
selected: halign_str(self.horizontal_alignment).to_string(), selected: halign_str(self.horizontal_alignment).to_string(),
@ -457,7 +458,7 @@ impl PropertyEditable for AttributeDefinition {
}, },
}, },
Property { Property {
label: "V-Align".into(), label: t!("V-Align").into_owned(),
field: "att_valign", field: "att_valign",
value: PropValue::Choice { value: PropValue::Choice {
selected: valign_str(self.vertical_alignment).to_string(), selected: valign_str(self.vertical_alignment).to_string(),
@ -467,25 +468,25 @@ impl PropertyEditable for AttributeDefinition {
.collect(), .collect(),
}, },
}, },
edit("Height", "att_h", self.height), edit(t!("Height").as_ref(), "att_h", self.height),
edit_angle("Rotation", "att_rot", self.rotation.to_degrees()), edit_angle(t!("Rotation").as_ref(), "att_rot", self.rotation.to_degrees()),
edit("Width factor", "att_wf", self.width_factor), edit(t!("Width factor").as_ref(), "att_wf", self.width_factor),
edit_angle("Obliquing", "att_ob", self.oblique_angle.to_degrees()), edit_angle(t!("Obliquing").as_ref(), "att_ob", self.oblique_angle.to_degrees()),
edit("Text alignment X", "att_ax", self.alignment_point.x), edit(t!("Text alignment X").as_ref(), "att_ax", self.alignment_point.x),
edit("Text alignment Y", "att_ay", self.alignment_point.y), edit(t!("Text alignment Y").as_ref(), "att_ay", self.alignment_point.y),
edit("Text alignment Z", "att_az", self.alignment_point.z), edit(t!("Text alignment Z").as_ref(), "att_az", self.alignment_point.z),
ro( ro(
"Boundary width", t!("Boundary width").as_ref(),
"att_field_len", "att_field_len",
self.field_length.to_string(), self.field_length.to_string(),
), ),
ro( ro(
"Upside down", t!("Upside down").as_ref(),
"att_upside_down", "att_upside_down",
bool_yn(self.text_generation_flags & 0x4 != 0), bool_yn(self.text_generation_flags & 0x4 != 0),
), ),
ro( ro(
"Backward", t!("Backward").as_ref(),
"att_backward", "att_backward",
bool_yn(self.text_generation_flags & 0x2 != 0), bool_yn(self.text_generation_flags & 0x2 != 0),
), ),
@ -499,27 +500,27 @@ impl PropertyEditable for AttributeDefinition {
} }
vec![ vec![
PropSection { PropSection {
title: "Text".into(), title: t!("Text").into_owned(),
props: text_props, props: text_props,
}, },
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Position X", "att_ix", self.insertion_point.x), edit(t!("Position X").as_ref(), "att_ix", self.insertion_point.x),
edit("Position Y", "att_iy", self.insertion_point.y), edit(t!("Position Y").as_ref(), "att_iy", self.insertion_point.y),
edit("Position Z", "att_iz", self.insertion_point.z), edit(t!("Position Z").as_ref(), "att_iz", self.insertion_point.z),
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
ro("Invisible", "att_invisible", bool_yn(self.flags.invisible)), ro(t!("Invisible").as_ref(), "att_invisible", bool_yn(self.flags.invisible)),
ro("Constant", "att_constant", bool_yn(self.flags.constant)), ro(t!("Constant").as_ref(), "att_constant", bool_yn(self.flags.constant)),
ro("Verify", "att_verify", bool_yn(self.flags.verify)), ro(t!("Verify").as_ref(), "att_verify", bool_yn(self.flags.verify)),
ro("Preset", "att_preset", bool_yn(self.flags.preset)), ro(t!("Preset").as_ref(), "att_preset", bool_yn(self.flags.preset)),
ro("Lock position", "att_lock_pos", bool_yn(self.lock_position)), ro(t!("Lock position").as_ref(), "att_lock_pos", bool_yn(self.lock_position)),
Property { Property {
label: "Multiple lines".into(), label: t!("Multiple lines").into_owned(),
field: "att_mtext_flag", field: "att_mtext_flag",
value: PropValue::Choice { value: PropValue::Choice {
selected: mtext_flag_str(self.mtext_flag).to_string(), selected: mtext_flag_str(self.mtext_flag).to_string(),
@ -686,21 +687,21 @@ impl PropertyEditable for AttributeEntity {
fn geometry_properties(&self, text_style_names: &[String]) -> Vec<PropSection> { fn geometry_properties(&self, text_style_names: &[String]) -> Vec<PropSection> {
vec![ vec![
PropSection { PropSection {
title: "Text".into(), title: t!("Text").into_owned(),
props: vec![ props: vec![
Property { Property {
label: "Tag".into(), label: t!("Tag").into_owned(),
field: "atte_tag", field: "atte_tag",
value: PropValue::EditText(self.tag.clone()), value: PropValue::EditText(self.tag.clone()),
}, },
ro("Prompt", "atte_prompt", String::new()), ro(t!("Prompt").as_ref(), "atte_prompt", String::new()),
Property { Property {
label: "Value".into(), label: t!("Value").into_owned(),
field: "atte_val", field: "atte_val",
value: PropValue::EditText(self.value.clone()), value: PropValue::EditText(self.value.clone()),
}, },
Property { Property {
label: "Style".into(), label: t!("Style").into_owned(),
field: "atte_style", field: "atte_style",
value: PropValue::Choice { value: PropValue::Choice {
selected: if self.text_style.trim().is_empty() { selected: if self.text_style.trim().is_empty() {
@ -712,7 +713,7 @@ impl PropertyEditable for AttributeEntity {
}, },
}, },
Property { Property {
label: "Justify".into(), label: t!("Justify").into_owned(),
field: "atte_halign", field: "atte_halign",
value: PropValue::Choice { value: PropValue::Choice {
selected: halign_str(self.horizontal_alignment).to_string(), selected: halign_str(self.horizontal_alignment).to_string(),
@ -723,7 +724,7 @@ impl PropertyEditable for AttributeEntity {
}, },
}, },
Property { Property {
label: "V-Align".into(), label: t!("V-Align").into_owned(),
field: "atte_valign", field: "atte_valign",
value: PropValue::Choice { value: PropValue::Choice {
selected: valign_str(self.vertical_alignment).to_string(), selected: valign_str(self.vertical_alignment).to_string(),
@ -734,56 +735,56 @@ impl PropertyEditable for AttributeEntity {
}, },
}, },
ro( ro(
"Annotative", t!("Annotative").as_ref(),
"atte_annotative", "atte_annotative",
bool_yn(self.flags.annotative), bool_yn(self.flags.annotative),
), ),
edit("Height", "atte_h", self.height), edit(t!("Height").as_ref(), "atte_h", self.height),
edit_angle("Rotation", "atte_rot", self.rotation.to_degrees()), edit_angle(t!("Rotation").as_ref(), "atte_rot", self.rotation.to_degrees()),
edit("Width factor", "atte_wf", self.width_factor), edit(t!("Width factor").as_ref(), "atte_wf", self.width_factor),
edit_angle("Obliquing", "atte_ob", self.oblique_angle.to_degrees()), edit_angle(t!("Obliquing").as_ref(), "atte_ob", self.oblique_angle.to_degrees()),
edit("Text alignment X", "atte_ax", self.alignment_point.x), edit(t!("Text alignment X").as_ref(), "atte_ax", self.alignment_point.x),
edit("Text alignment Y", "atte_ay", self.alignment_point.y), edit(t!("Text alignment Y").as_ref(), "atte_ay", self.alignment_point.y),
edit("Text alignment Z", "atte_az", self.alignment_point.z), edit(t!("Text alignment Z").as_ref(), "atte_az", self.alignment_point.z),
ro( ro(
"Boundary width", t!("Boundary width").as_ref(),
"atte_field_len", "atte_field_len",
self.field_length.to_string(), self.field_length.to_string(),
), ),
], ],
}, },
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Position X", "atte_ix", self.insertion_point.x), edit(t!("Position X").as_ref(), "atte_ix", self.insertion_point.x),
edit("Position Y", "atte_iy", self.insertion_point.y), edit(t!("Position Y").as_ref(), "atte_iy", self.insertion_point.y),
edit("Position Z", "atte_iz", self.insertion_point.z), edit(t!("Position Z").as_ref(), "atte_iz", self.insertion_point.z),
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
ro( ro(
"Upside down", t!("Upside down").as_ref(),
"atte_upside_down", "atte_upside_down",
bool_yn(self.text_generation_flags & 0x4 != 0), bool_yn(self.text_generation_flags & 0x4 != 0),
), ),
ro( ro(
"Backward", t!("Backward").as_ref(),
"atte_backward", "atte_backward",
bool_yn(self.text_generation_flags & 0x2 != 0), bool_yn(self.text_generation_flags & 0x2 != 0),
), ),
ro("Invisible", "atte_invisible", bool_yn(self.flags.invisible)), ro(t!("Invisible").as_ref(), "atte_invisible", bool_yn(self.flags.invisible)),
ro( ro(
"Multiple lines", t!("Multiple lines").as_ref(),
"atte_mtext_flag", "atte_mtext_flag",
mtext_flag_str(self.mtext_flag), mtext_flag_str(self.mtext_flag),
), ),
ro("Constant", "atte_constant", bool_yn(self.flags.constant)), ro(t!("Constant").as_ref(), "atte_constant", bool_yn(self.flags.constant)),
ro("Verify", "atte_verify", bool_yn(self.flags.verify)), ro(t!("Verify").as_ref(), "atte_verify", bool_yn(self.flags.verify)),
ro("Preset", "atte_preset", bool_yn(self.flags.preset)), ro(t!("Preset").as_ref(), "atte_preset", bool_yn(self.flags.preset)),
ro( ro(
"Lock position", t!("Lock position").as_ref(),
"atte_lock_pos", "atte_lock_pos",
bool_yn(self.lock_position), bool_yn(self.lock_position),
), ),

View file

@ -1,4 +1,5 @@
use acadrust::entities::Circle; use acadrust::entities::Circle;
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{ use crate::entities::common::{
@ -134,18 +135,18 @@ fn properties(circle: &Circle) -> Vec<PropSection> {
use std::f64::consts::PI; use std::f64::consts::PI;
let r = circle.radius; let r = circle.radius;
vec![PropSection { vec![PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Center X", "center_x", circle.center.x), edit(t!("Center X").as_ref(), "center_x", circle.center.x),
edit("Center Y", "center_y", circle.center.y), edit(t!("Center Y").as_ref(), "center_y", circle.center.y),
edit("Center Z", "center_z", circle.center.z), edit(t!("Center Z").as_ref(), "center_z", circle.center.z),
edit("Radius", "radius", r), edit(t!("Radius").as_ref(), "radius", r),
edit("Diameter", "diameter", r * 2.0), edit(t!("Diameter").as_ref(), "diameter", r * 2.0),
edit("Circumference", "circumference", 2.0 * PI * r), edit(t!("Circumference").as_ref(), "circumference", 2.0 * PI * r),
edit("Area", "area", PI * r * r), edit(t!("Area").as_ref(), "area", PI * r * r),
ro("Normal X", "normal_x", format!("{:.4}", circle.normal.x)), ro(t!("Normal X").as_ref(), "normal_x", format!("{:.4}", circle.normal.x)),
ro("Normal Y", "normal_y", format!("{:.4}", circle.normal.y)), ro(t!("Normal Y").as_ref(), "normal_y", format!("{:.4}", circle.normal.y)),
ro("Normal Z", "normal_z", format!("{:.4}", circle.normal.z)), ro(t!("Normal Z").as_ref(), "normal_z", format!("{:.4}", circle.normal.z)),
], ],
}] }]
} }

View file

@ -187,7 +187,7 @@ pub fn triangle_grip(id: usize, world: glam::DVec3) -> GripDef {
/// LINEAR formatter, so LUNITS=Architectural showed a block rotation as /// LINEAR formatter, so LUNITS=Architectural showed a block rotation as
/// feet-and-inches and the string wouldn't parse back (#297). Value in /// feet-and-inches and the string wouldn't parse back (#297). Value in
/// DEGREES, matching every angle call site. /// DEGREES, matching every angle call site.
pub fn edit_angle_prop(label: &'static str, field: &'static str, value_deg: f64) -> Property { pub fn edit_angle_prop(label: &str, field: &'static str, value_deg: f64) -> Property {
Property { Property {
label: label.into(), label: label.into(),
field, field,
@ -195,7 +195,7 @@ pub fn edit_angle_prop(label: &'static str, field: &'static str, value_deg: f64)
} }
} }
pub fn edit_prop(label: &'static str, field: &'static str, value: f64) -> Property { pub fn edit_prop(label: &str, field: &'static str, value: f64) -> Property {
Property { Property {
label: label.into(), label: label.into(),
field, field,
@ -203,7 +203,7 @@ pub fn edit_prop(label: &'static str, field: &'static str, value: f64) -> Proper
} }
} }
pub fn ro_prop(label: &'static str, field: &'static str, value: impl Into<String>) -> Property { pub fn ro_prop(label: &str, field: &'static str, value: impl Into<String>) -> Property {
Property { Property {
label: label.into(), label: label.into(),
field, field,
@ -215,7 +215,7 @@ pub fn ro_prop(label: &'static str, field: &'static str, value: impl Into<String
/// read-only value using the same length formatting. Used where a field's /// read-only value using the same length formatting. Used where a field's
/// editability depends on entity state (e.g. a text point that is only live for /// editability depends on entity state (e.g. a text point that is only live for
/// certain justifications, or an MText column dimension). /// certain justifications, or an MText column dimension).
pub fn num_prop(label: &'static str, field: &'static str, value: f64, editable: bool) -> Property { pub fn num_prop(label: &str, field: &'static str, value: f64, editable: bool) -> Property {
if editable { if editable {
edit_prop(label, field, value) edit_prop(label, field, value)
} else { } else {
@ -226,7 +226,7 @@ pub fn num_prop(label: &'static str, field: &'static str, value: f64, editable:
/// A ◀ / ▶ index navigator row (e.g. a polyline's Current Vertex). `display` is /// A ◀ / ▶ index navigator row (e.g. a polyline's Current Vertex). `display` is
/// the label shown between the arrows (e.g. "2 / 7"). /// the label shown between the arrows (e.g. "2 / 7").
pub fn stepper_prop( pub fn stepper_prop(
label: &'static str, label: &str,
field: &'static str, field: &'static str,
display: impl Into<String>, display: impl Into<String>,
) -> Property { ) -> Property {

View file

@ -12,46 +12,47 @@ use crate::entities::common::{
}; };
use crate::entities::traits::{Grippable, PropertyEditable, Transformable}; use crate::entities::traits::{Grippable, PropertyEditable, Transformable};
use crate::scene::model::object::{GripApply, GripDef, PropSection}; use crate::scene::model::object::{GripApply, GripDef, PropSection};
use crate::t;
fn base_props(base: &DimensionBase) -> Vec<crate::scene::model::object::Property> { fn base_props(base: &DimensionBase) -> Vec<crate::scene::model::object::Property> {
vec![ vec![
crate::scene::model::object::Property { crate::scene::model::object::Property {
label: "Text".into(), label: t!("Text").into_owned(),
field: "text", field: "text",
value: crate::scene::model::object::PropValue::EditText(base.text.clone()), value: crate::scene::model::object::PropValue::EditText(base.text.clone()),
}, },
crate::scene::model::object::Property { crate::scene::model::object::Property {
label: "User Text".into(), label: t!("User Text").into_owned(),
field: "user_text", field: "user_text",
value: crate::scene::model::object::PropValue::EditText( value: crate::scene::model::object::PropValue::EditText(
base.user_text.clone().unwrap_or_default(), base.user_text.clone().unwrap_or_default(),
), ),
}, },
crate::scene::model::object::Property { crate::scene::model::object::Property {
label: "Style".into(), label: t!("Style").into_owned(),
field: "style_name", field: "style_name",
value: crate::scene::model::object::PropValue::EditText(base.style_name.clone()), value: crate::scene::model::object::PropValue::EditText(base.style_name.clone()),
}, },
edit("Text X", "text_x", base.text_middle_point.x), edit(t!("Text X").as_ref(), "text_x", base.text_middle_point.x),
edit("Text Y", "text_y", base.text_middle_point.y), edit(t!("Text Y").as_ref(), "text_y", base.text_middle_point.y),
edit("Text Z", "text_z", base.text_middle_point.z), edit(t!("Text Z").as_ref(), "text_z", base.text_middle_point.z),
edit( edit(
"Text Rotation (deg)", t!("Text Rotation (deg)").as_ref(),
"text_rotation", "text_rotation",
base.text_rotation.to_degrees(), base.text_rotation.to_degrees(),
), ),
edit( edit(
"Horizontal Dir (deg)", t!("Horizontal Dir (deg)").as_ref(),
"horizontal_direction", "horizontal_direction",
base.horizontal_direction.to_degrees(), base.horizontal_direction.to_degrees(),
), ),
edit( edit(
"Line Spacing", t!("Line Spacing").as_ref(),
"line_spacing_factor", "line_spacing_factor",
base.line_spacing_factor, base.line_spacing_factor,
), ),
ro( ro(
"Measurement", t!("Measurement").as_ref(),
"measurement", "measurement",
format!("{:.4}", base.actual_measurement), format!("{:.4}", base.actual_measurement),
), ),
@ -68,7 +69,7 @@ fn properties(dim: &Dimension) -> Vec<PropSection> {
d.definition_point, d.definition_point,
)); ));
props.push(edit( props.push(edit(
"Ext Rotation (deg)", t!("Ext Rotation (deg)").as_ref(),
"ext_line_rotation", "ext_line_rotation",
d.ext_line_rotation.to_degrees(), d.ext_line_rotation.to_degrees(),
)); ));
@ -79,20 +80,20 @@ fn properties(dim: &Dimension) -> Vec<PropSection> {
d.second_point, d.second_point,
d.definition_point, d.definition_point,
)); ));
props.push(edit_angle("Rotation", "rotation", d.rotation.to_degrees())); props.push(edit_angle(t!("Rotation").as_ref(), "rotation", d.rotation.to_degrees()));
props.push(edit( props.push(edit(
"Ext Rotation (deg)", t!("Ext Rotation (deg)").as_ref(),
"ext_line_rotation", "ext_line_rotation",
d.ext_line_rotation.to_degrees(), d.ext_line_rotation.to_degrees(),
)); ));
} }
Dimension::Radius(d) => { Dimension::Radius(d) => {
props.extend(radius_like_props(d.angle_vertex, d.definition_point)); props.extend(radius_like_props(d.angle_vertex, d.definition_point));
props.push(edit("Leader Length", "leader_length", d.leader_length)); props.push(edit(t!("Leader Length").as_ref(), "leader_length", d.leader_length));
} }
Dimension::Diameter(d) => { Dimension::Diameter(d) => {
props.extend(radius_like_props(d.angle_vertex, d.definition_point)); props.extend(radius_like_props(d.angle_vertex, d.definition_point));
props.push(edit("Leader Length", "leader_length", d.leader_length)); props.push(edit(t!("Leader Length").as_ref(), "leader_length", d.leader_length));
} }
Dimension::Angular2Ln(d) => { Dimension::Angular2Ln(d) => {
props.extend(angular_props( props.extend(angular_props(
@ -101,9 +102,9 @@ fn properties(dim: &Dimension) -> Vec<PropSection> {
d.second_point, d.second_point,
d.definition_point, d.definition_point,
)); ));
props.push(edit("Arc X", "dimension_arc_x", d.dimension_arc.x)); props.push(edit(t!("Arc X").as_ref(), "dimension_arc_x", d.dimension_arc.x));
props.push(edit("Arc Y", "dimension_arc_y", d.dimension_arc.y)); props.push(edit(t!("Arc Y").as_ref(), "dimension_arc_y", d.dimension_arc.y));
props.push(edit("Arc Z", "dimension_arc_z", d.dimension_arc.z)); props.push(edit(t!("Arc Z").as_ref(), "dimension_arc_z", d.dimension_arc.z));
} }
Dimension::Angular3Pt(d) => { Dimension::Angular3Pt(d) => {
props.extend(angular_props( props.extend(angular_props(
@ -114,17 +115,17 @@ fn properties(dim: &Dimension) -> Vec<PropSection> {
)); ));
} }
Dimension::Ordinate(d) => { Dimension::Ordinate(d) => {
props.push(edit("Origin X", "definition_x", d.definition_point.x)); props.push(edit(t!("Origin X").as_ref(), "definition_x", d.definition_point.x));
props.push(edit("Origin Y", "definition_y", d.definition_point.y)); props.push(edit(t!("Origin Y").as_ref(), "definition_y", d.definition_point.y));
props.push(edit("Origin Z", "definition_z", d.definition_point.z)); props.push(edit(t!("Origin Z").as_ref(), "definition_z", d.definition_point.z));
props.push(edit("Feature X", "feature_x", d.feature_location.x)); props.push(edit(t!("Feature X").as_ref(), "feature_x", d.feature_location.x));
props.push(edit("Feature Y", "feature_y", d.feature_location.y)); props.push(edit(t!("Feature Y").as_ref(), "feature_y", d.feature_location.y));
props.push(edit("Feature Z", "feature_z", d.feature_location.z)); props.push(edit(t!("Feature Z").as_ref(), "feature_z", d.feature_location.z));
props.push(edit("Leader X", "leader_x", d.leader_endpoint.x)); props.push(edit(t!("Leader X").as_ref(), "leader_x", d.leader_endpoint.x));
props.push(edit("Leader Y", "leader_y", d.leader_endpoint.y)); props.push(edit(t!("Leader Y").as_ref(), "leader_y", d.leader_endpoint.y));
props.push(edit("Leader Z", "leader_z", d.leader_endpoint.z)); props.push(edit(t!("Leader Z").as_ref(), "leader_z", d.leader_endpoint.z));
props.push(ro( props.push(ro(
"Ordinate Type", t!("Ordinate Type").as_ref(),
"ordinate_type", "ordinate_type",
if d.is_ordinate_type_x { "X" } else { "Y" }, if d.is_ordinate_type_x { "X" } else { "Y" },
)); ));
@ -137,42 +138,42 @@ fn properties(dim: &Dimension) -> Vec<PropSection> {
d.definition_point, d.definition_point,
)); ));
props.push(edit( props.push(edit(
"Arc Start (deg)", t!("Arc Start (deg)").as_ref(),
"arc_start_parameter", "arc_start_parameter",
d.arc_start_parameter.to_degrees(), d.arc_start_parameter.to_degrees(),
)); ));
props.push(edit( props.push(edit(
"Arc End (deg)", t!("Arc End (deg)").as_ref(),
"arc_end_parameter", "arc_end_parameter",
d.arc_end_parameter.to_degrees(), d.arc_end_parameter.to_degrees(),
)); ));
props.push(ro("Partial", "is_partial", d.is_partial.to_string())); props.push(ro(t!("Partial").as_ref(), "is_partial", d.is_partial.to_string()));
props.push(ro("Has Leader", "has_leader", d.has_leader.to_string())); props.push(ro(t!("Has Leader").as_ref(), "has_leader", d.has_leader.to_string()));
props.push(edit("Leader 1 X", "leader1_x", d.first_leader_point.x)); props.push(edit(t!("Leader 1 X").as_ref(), "leader1_x", d.first_leader_point.x));
props.push(edit("Leader 1 Y", "leader1_y", d.first_leader_point.y)); props.push(edit(t!("Leader 1 Y").as_ref(), "leader1_y", d.first_leader_point.y));
props.push(edit("Leader 1 Z", "leader1_z", d.first_leader_point.z)); props.push(edit(t!("Leader 1 Z").as_ref(), "leader1_z", d.first_leader_point.z));
props.push(edit("Leader 2 X", "leader2_x", d.second_leader_point.x)); props.push(edit(t!("Leader 2 X").as_ref(), "leader2_x", d.second_leader_point.x));
props.push(edit("Leader 2 Y", "leader2_y", d.second_leader_point.y)); props.push(edit(t!("Leader 2 Y").as_ref(), "leader2_y", d.second_leader_point.y));
props.push(edit("Leader 2 Z", "leader2_z", d.second_leader_point.z)); props.push(edit(t!("Leader 2 Z").as_ref(), "leader2_z", d.second_leader_point.z));
} }
Dimension::LargeRadial(d) => { Dimension::LargeRadial(d) => {
props.push(edit("Center X", "definition_x", d.definition_point.x)); props.push(edit(t!("Center X").as_ref(), "definition_x", d.definition_point.x));
props.push(edit("Center Y", "definition_y", d.definition_point.y)); props.push(edit(t!("Center Y").as_ref(), "definition_y", d.definition_point.y));
props.push(edit("Center Z", "definition_z", d.definition_point.z)); props.push(edit(t!("Center Z").as_ref(), "definition_z", d.definition_point.z));
props.push(edit("Chord X", "chord_x", d.chord_point.x)); props.push(edit(t!("Chord X").as_ref(), "chord_x", d.chord_point.x));
props.push(edit("Chord Y", "chord_y", d.chord_point.y)); props.push(edit(t!("Chord Y").as_ref(), "chord_y", d.chord_point.y));
props.push(edit("Chord Z", "chord_z", d.chord_point.z)); props.push(edit(t!("Chord Z").as_ref(), "chord_z", d.chord_point.z));
props.push(edit("Override Center X", "override_x", d.override_center.x)); props.push(edit(t!("Override Center X").as_ref(), "override_x", d.override_center.x));
props.push(edit("Override Center Y", "override_y", d.override_center.y)); props.push(edit(t!("Override Center Y").as_ref(), "override_y", d.override_center.y));
props.push(edit("Override Center Z", "override_z", d.override_center.z)); props.push(edit(t!("Override Center Z").as_ref(), "override_z", d.override_center.z));
props.push(edit("Jog X", "jog_x", d.jog_point.x)); props.push(edit(t!("Jog X").as_ref(), "jog_x", d.jog_point.x));
props.push(edit("Jog Y", "jog_y", d.jog_point.y)); props.push(edit(t!("Jog Y").as_ref(), "jog_y", d.jog_point.y));
props.push(edit("Jog Z", "jog_z", d.jog_point.z)); props.push(edit(t!("Jog Z").as_ref(), "jog_z", d.jog_point.z));
props.push(edit_angle("Jog Angle", "jog_angle", d.jog_angle.to_degrees())); props.push(edit_angle(t!("Jog Angle").as_ref(), "jog_angle", d.jog_angle.to_degrees()));
} }
} }
vec![PropSection { vec![PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props, props,
}] }]
} }
@ -183,15 +184,15 @@ fn linear_like_props(
definition: acadrust::types::Vector3, definition: acadrust::types::Vector3,
) -> Vec<crate::scene::model::object::Property> { ) -> Vec<crate::scene::model::object::Property> {
vec![ vec![
edit("First X", "first_x", first.x), edit(t!("First X").as_ref(), "first_x", first.x),
edit("First Y", "first_y", first.y), edit(t!("First Y").as_ref(), "first_y", first.y),
edit("First Z", "first_z", first.z), edit(t!("First Z").as_ref(), "first_z", first.z),
edit("Second X", "second_x", second.x), edit(t!("Second X").as_ref(), "second_x", second.x),
edit("Second Y", "second_y", second.y), edit(t!("Second Y").as_ref(), "second_y", second.y),
edit("Second Z", "second_z", second.z), edit(t!("Second Z").as_ref(), "second_z", second.z),
edit("Definition X", "definition_x", definition.x), edit(t!("Definition X").as_ref(), "definition_x", definition.x),
edit("Definition Y", "definition_y", definition.y), edit(t!("Definition Y").as_ref(), "definition_y", definition.y),
edit("Definition Z", "definition_z", definition.z), edit(t!("Definition Z").as_ref(), "definition_z", definition.z),
] ]
} }
@ -200,12 +201,12 @@ fn radius_like_props(
point: acadrust::types::Vector3, point: acadrust::types::Vector3,
) -> Vec<crate::scene::model::object::Property> { ) -> Vec<crate::scene::model::object::Property> {
vec![ vec![
edit("Center X", "center_x", center.x), edit(t!("Center X").as_ref(), "center_x", center.x),
edit("Center Y", "center_y", center.y), edit(t!("Center Y").as_ref(), "center_y", center.y),
edit("Center Z", "center_z", center.z), edit(t!("Center Z").as_ref(), "center_z", center.z),
edit("Point X", "point_x", point.x), edit(t!("Point X").as_ref(), "point_x", point.x),
edit("Point Y", "point_y", point.y), edit(t!("Point Y").as_ref(), "point_y", point.y),
edit("Point Z", "point_z", point.z), edit(t!("Point Z").as_ref(), "point_z", point.z),
] ]
} }
@ -216,18 +217,18 @@ fn angular_props(
definition: acadrust::types::Vector3, definition: acadrust::types::Vector3,
) -> Vec<crate::scene::model::object::Property> { ) -> Vec<crate::scene::model::object::Property> {
vec![ vec![
edit("Vertex X", "vertex_x", vertex.x), edit(t!("Vertex X").as_ref(), "vertex_x", vertex.x),
edit("Vertex Y", "vertex_y", vertex.y), edit(t!("Vertex Y").as_ref(), "vertex_y", vertex.y),
edit("Vertex Z", "vertex_z", vertex.z), edit(t!("Vertex Z").as_ref(), "vertex_z", vertex.z),
edit("First X", "first_x", first.x), edit(t!("First X").as_ref(), "first_x", first.x),
edit("First Y", "first_y", first.y), edit(t!("First Y").as_ref(), "first_y", first.y),
edit("First Z", "first_z", first.z), edit(t!("First Z").as_ref(), "first_z", first.z),
edit("Second X", "second_x", second.x), edit(t!("Second X").as_ref(), "second_x", second.x),
edit("Second Y", "second_y", second.y), edit(t!("Second Y").as_ref(), "second_y", second.y),
edit("Second Z", "second_z", second.z), edit(t!("Second Z").as_ref(), "second_z", second.z),
edit("Definition X", "definition_x", definition.x), edit(t!("Definition X").as_ref(), "definition_x", definition.x),
edit("Definition Y", "definition_y", definition.y), edit(t!("Definition Y").as_ref(), "definition_y", definition.y),
edit("Definition Z", "definition_z", definition.z), edit(t!("Definition Z").as_ref(), "definition_z", definition.z),
] ]
} }
@ -1150,133 +1151,133 @@ pub fn style_sections(style: &DimStyle) -> Vec<crate::scene::model::object::Prop
vec![ vec![
PropSection { PropSection {
title: "Lines & Arrows".into(), title: t!("Lines & Arrows").into_owned(),
props: vec![ props: vec![
ro("Arrow size", "dim_arrow_size", f(s.dimasz)), ro(t!("Arrow size").as_ref(), "dim_arrow_size", f(s.dimasz)),
ro("Dim line color", "dim_line_color", s.dimclrd.to_string()), ro(t!("Dim line color").as_ref(), "dim_line_color", s.dimclrd.to_string()),
ro( ro(
"Dim line lineweight", t!("Dim line lineweight").as_ref(),
"dim_line_lineweight", "dim_line_lineweight",
s.dimlwd.to_string(), s.dimlwd.to_string(),
), ),
ro("Ext line color", "dim_ext_line_color", s.dimclre.to_string()), ro(t!("Ext line color").as_ref(), "dim_ext_line_color", s.dimclre.to_string()),
ro( ro(
"Ext line lineweight", t!("Ext line lineweight").as_ref(),
"dim_ext_line_lineweight", "dim_ext_line_lineweight",
s.dimlwe.to_string(), s.dimlwe.to_string(),
), ),
ro("Dim line 1", "dim_line_1", onoff(!s.dimsd1)), ro(t!("Dim line 1").as_ref(), "dim_line_1", onoff(!s.dimsd1)),
ro("Dim line 2", "dim_line_2", onoff(!s.dimsd2)), ro(t!("Dim line 2").as_ref(), "dim_line_2", onoff(!s.dimsd2)),
ro("Ext line 1", "dim_ext_line_1", onoff(!s.dimse1)), ro(t!("Ext line 1").as_ref(), "dim_ext_line_1", onoff(!s.dimse1)),
ro("Ext line 2", "dim_ext_line_2", onoff(!s.dimse2)), ro(t!("Ext line 2").as_ref(), "dim_ext_line_2", onoff(!s.dimse2)),
ro("Dim line ext", "dim_line_ext", f(s.dimdle)), ro(t!("Dim line ext").as_ref(), "dim_line_ext", f(s.dimdle)),
ro("Ext line ext", "dim_ext_line_ext", f(s.dimexe)), ro(t!("Ext line ext").as_ref(), "dim_ext_line_ext", f(s.dimexe)),
ro("Ext line offset", "dim_ext_line_offset", f(s.dimexo)), ro(t!("Ext line offset").as_ref(), "dim_ext_line_offset", f(s.dimexo)),
ro("Ext line fixed", "dim_ext_line_fixed", yn(s.dimfxlon)), ro(t!("Ext line fixed").as_ref(), "dim_ext_line_fixed", yn(s.dimfxlon)),
ro( ro(
"Ext line fixed length", t!("Ext line fixed length").as_ref(),
"dim_ext_line_fixed_length", "dim_ext_line_fixed_length",
f(s.dimfxl), f(s.dimfxl),
), ),
], ],
}, },
PropSection { PropSection {
title: "Text".into(), title: t!("Text").into_owned(),
props: vec![ props: vec![
ro("Fill color", "dim_text_fill_color", s.dimtfillclr.to_string()), ro(t!("Fill color").as_ref(), "dim_text_fill_color", s.dimtfillclr.to_string()),
ro("Text color", "dim_text_color", s.dimclrt.to_string()), ro(t!("Text color").as_ref(), "dim_text_color", s.dimclrt.to_string()),
ro("Text height", "dim_text_height", f(s.dimtxt)), ro(t!("Text height").as_ref(), "dim_text_height", f(s.dimtxt)),
ro("Text offset", "dim_text_offset", f(s.dimgap)), ro(t!("Text offset").as_ref(), "dim_text_offset", f(s.dimgap)),
ro("Text pos vert", "dim_text_pos_vert", s.dimtad.to_string()), ro(t!("Text pos vert").as_ref(), "dim_text_pos_vert", s.dimtad.to_string()),
ro("Text pos hor", "dim_text_pos_hor", s.dimjust.to_string()), ro(t!("Text pos hor").as_ref(), "dim_text_pos_hor", s.dimjust.to_string()),
ro("Text outside align", "dim_text_outside_align", yn(s.dimtoh)), ro(t!("Text outside align").as_ref(), "dim_text_outside_align", yn(s.dimtoh)),
ro("Text inside align", "dim_text_inside_align", yn(s.dimtih)), ro(t!("Text inside align").as_ref(), "dim_text_inside_align", yn(s.dimtih)),
ro("Text style", "dim_text_style", s.dimtxsty.clone()), ro(t!("Text style").as_ref(), "dim_text_style", s.dimtxsty.clone()),
], ],
}, },
PropSection { PropSection {
title: "Fit".into(), title: t!("Fit").into_owned(),
props: vec![ props: vec![
ro("Fit", "dim_fit", s.dimatfit.to_string()), ro(t!("Fit").as_ref(), "dim_fit", s.dimatfit.to_string()),
ro("Text inside", "dim_text_inside", yn(s.dimtix)), ro(t!("Text inside").as_ref(), "dim_text_inside", yn(s.dimtix)),
ro("Text movement", "dim_text_movement", s.dimtmove.to_string()), ro(t!("Text movement").as_ref(), "dim_text_movement", s.dimtmove.to_string()),
ro("Dim scale overall", "dim_scale_overall", f(s.dimscale)), ro(t!("Dim scale overall").as_ref(), "dim_scale_overall", f(s.dimscale)),
ro("Dim line forced", "dim_line_forced", yn(s.dimtofl)), ro(t!("Dim line forced").as_ref(), "dim_line_forced", yn(s.dimtofl)),
ro("Dim line inside", "dim_line_inside", yn(s.dimsoxd)), ro(t!("Dim line inside").as_ref(), "dim_line_inside", yn(s.dimsoxd)),
], ],
}, },
PropSection { PropSection {
title: "Primary Units".into(), title: t!("Primary Units").into_owned(),
props: vec![ props: vec![
ro("Dim units", "dim_units", s.dimlunit.to_string()), ro(t!("Dim units").as_ref(), "dim_units", s.dimlunit.to_string()),
ro("Precision", "dim_precision", s.dimdec.to_string()), ro(t!("Precision").as_ref(), "dim_precision", s.dimdec.to_string()),
ro("Decimal separator", "dim_decimal_separator", dsep), ro(t!("Decimal separator").as_ref(), "dim_decimal_separator", dsep),
ro("Dim prefix/suffix", "dim_prefix_suffix", s.dimpost.clone()), ro(t!("Dim prefix/suffix").as_ref(), "dim_prefix_suffix", s.dimpost.clone()),
ro("Dim roundoff", "dim_roundoff", f(s.dimrnd)), ro(t!("Dim roundoff").as_ref(), "dim_roundoff", f(s.dimrnd)),
ro( ro(
"Suppress leading zeros", t!("Suppress leading zeros").as_ref(),
"dim_suppress_leading_zeros", "dim_suppress_leading_zeros",
yn(s.dimzin & 4 != 0), yn(s.dimzin & 4 != 0),
), ),
ro( ro(
"Suppress trailing zeros", t!("Suppress trailing zeros").as_ref(),
"dim_suppress_trailing_zeros", "dim_suppress_trailing_zeros",
yn(s.dimzin & 8 != 0), yn(s.dimzin & 8 != 0),
), ),
ro("Dim scale linear", "dim_scale_linear", f(s.dimlfac)), ro(t!("Dim scale linear").as_ref(), "dim_scale_linear", f(s.dimlfac)),
ro("Angle format", "dim_angle_format", s.dimaunit.to_string()), ro(t!("Angle format").as_ref(), "dim_angle_format", s.dimaunit.to_string()),
ro("Angle precision", "dim_angle_precision", s.dimadec.to_string()), ro(t!("Angle precision").as_ref(), "dim_angle_precision", s.dimadec.to_string()),
], ],
}, },
PropSection { PropSection {
title: "Alternate Units".into(), title: t!("Alternate Units").into_owned(),
props: vec![ props: vec![
ro("Alt enabled", "dim_alt_enabled", yn(s.dimalt)), ro(t!("Alt enabled").as_ref(), "dim_alt_enabled", yn(s.dimalt)),
ro("Alt format", "dim_alt_format", s.dimaltu.to_string()), ro(t!("Alt format").as_ref(), "dim_alt_format", s.dimaltu.to_string()),
ro("Alt precision", "dim_alt_precision", s.dimaltd.to_string()), ro(t!("Alt precision").as_ref(), "dim_alt_precision", s.dimaltd.to_string()),
ro("Alt scale factor", "dim_alt_scale_factor", f(s.dimaltf)), ro(t!("Alt scale factor").as_ref(), "dim_alt_scale_factor", f(s.dimaltf)),
ro("Alt roundoff", "dim_alt_roundoff", f(s.dimaltrnd)), ro(t!("Alt roundoff").as_ref(), "dim_alt_roundoff", f(s.dimaltrnd)),
ro("Alt prefix/suffix", "dim_alt_prefix_suffix", s.dimapost.clone()), ro(t!("Alt prefix/suffix").as_ref(), "dim_alt_prefix_suffix", s.dimapost.clone()),
ro( ro(
"Alt suppress leading zeros", t!("Alt suppress leading zeros").as_ref(),
"dim_alt_suppress_leading_zeros", "dim_alt_suppress_leading_zeros",
yn(s.dimaltz & 4 != 0), yn(s.dimaltz & 4 != 0),
), ),
ro( ro(
"Alt suppress trailing zeros", t!("Alt suppress trailing zeros").as_ref(),
"dim_alt_suppress_trailing_zeros", "dim_alt_suppress_trailing_zeros",
yn(s.dimaltz & 8 != 0), yn(s.dimaltz & 8 != 0),
), ),
], ],
}, },
PropSection { PropSection {
title: "Tolerances".into(), title: t!("Tolerances").into_owned(),
props: vec![ props: vec![
ro("Tolerance display", "dim_tolerance_display", tol_display), ro(t!("Tolerance display").as_ref(), "dim_tolerance_display", tol_display),
ro("Tolerance limit lower", "dim_tolerance_limit_lower", f(s.dimtm)), ro(t!("Tolerance limit lower").as_ref(), "dim_tolerance_limit_lower", f(s.dimtm)),
ro("Tolerance limit upper", "dim_tolerance_limit_upper", f(s.dimtp)), ro(t!("Tolerance limit upper").as_ref(), "dim_tolerance_limit_upper", f(s.dimtp)),
ro( ro(
"Tolerance precision", t!("Tolerance precision").as_ref(),
"dim_tolerance_precision", "dim_tolerance_precision",
s.dimtdec.to_string(), s.dimtdec.to_string(),
), ),
ro( ro(
"Tolerance text height", t!("Tolerance text height").as_ref(),
"dim_tolerance_text_height", "dim_tolerance_text_height",
f(s.dimtfac), f(s.dimtfac),
), ),
ro( ro(
"Tolerance pos vert", t!("Tolerance pos vert").as_ref(),
"dim_tolerance_pos_vert", "dim_tolerance_pos_vert",
s.dimtolj.to_string(), s.dimtolj.to_string(),
), ),
ro( ro(
"Tolerance suppress leading zeros", t!("Tolerance suppress leading zeros").as_ref(),
"dim_tolerance_suppress_leading_zeros", "dim_tolerance_suppress_leading_zeros",
yn(s.dimtzin & 4 != 0), yn(s.dimtzin & 4 != 0),
), ),
ro( ro(
"Tolerance suppress trailing zeros", t!("Tolerance suppress trailing zeros").as_ref(),
"dim_tolerance_suppress_trailing_zeros", "dim_tolerance_suppress_trailing_zeros",
yn(s.dimtzin & 8 != 0), yn(s.dimtzin & 8 != 0),
), ),

View file

@ -1,4 +1,5 @@
use acadrust::entities::Ellipse; use acadrust::entities::Ellipse;
use crate::t;
use truck_modeling::{builder, BSplineCurve, Curve, Edge, KnotVec, Point3, Wire}; use truck_modeling::{builder, BSplineCurve, Curve, Edge, KnotVec, Point3, Wire};
use crate::command::EntityTransform; use crate::command::EntityTransform;
@ -194,35 +195,35 @@ fn properties(ell: &Ellipse) -> Vec<PropSection> {
let props = ell.mass_props(); let props = ell.mass_props();
vec![PropSection { vec![PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
ro("Start X", "start_x", format!("{:.4}", start.x)), ro(t!("Start X").as_ref(), "start_x", format!("{:.4}", start.x)),
ro("Start Y", "start_y", format!("{:.4}", start.y)), ro(t!("Start Y").as_ref(), "start_y", format!("{:.4}", start.y)),
ro("Start Z", "start_z", format!("{:.4}", start.z)), ro(t!("Start Z").as_ref(), "start_z", format!("{:.4}", start.z)),
edit("Center X", "center_x", ell.center.x), edit(t!("Center X").as_ref(), "center_x", ell.center.x),
edit("Center Y", "center_y", ell.center.y), edit(t!("Center Y").as_ref(), "center_y", ell.center.y),
edit("Center Z", "center_z", ell.center.z), edit(t!("Center Z").as_ref(), "center_z", ell.center.z),
ro("End X", "end_x", format!("{:.4}", end.x)), ro(t!("End X").as_ref(), "end_x", format!("{:.4}", end.x)),
ro("End Y", "end_y", format!("{:.4}", end.y)), ro(t!("End Y").as_ref(), "end_y", format!("{:.4}", end.y)),
ro("End Z", "end_z", format!("{:.4}", end.z)), ro(t!("End Z").as_ref(), "end_z", format!("{:.4}", end.z)),
edit("Major radius", "major_r", r_major), edit(t!("Major radius").as_ref(), "major_r", r_major),
edit("Minor radius", "minor_r", r_minor), edit(t!("Minor radius").as_ref(), "minor_r", r_minor),
edit("Radius ratio", "ratio", ell.minor_axis_ratio), edit(t!("Radius ratio").as_ref(), "ratio", ell.minor_axis_ratio),
edit("Start angle", "start_angle", start_angle), edit(t!("Start angle").as_ref(), "start_angle", start_angle),
edit("End angle", "end_angle", end_angle), edit(t!("End angle").as_ref(), "end_angle", end_angle),
ro("Major axis vector X", "major_x", format!("{:.4}", major_vec.x)), ro(t!("Major axis vector X").as_ref(), "major_x", format!("{:.4}", major_vec.x)),
ro("Major axis vector Y", "major_y", format!("{:.4}", major_vec.y)), ro(t!("Major axis vector Y").as_ref(), "major_y", format!("{:.4}", major_vec.y)),
ro("Major axis vector Z", "major_z", format!("{:.4}", major_vec.z)), ro(t!("Major axis vector Z").as_ref(), "major_z", format!("{:.4}", major_vec.z)),
ro("Minor axis vector X", "minor_x", format!("{:.4}", minor_vec.x)), ro(t!("Minor axis vector X").as_ref(), "minor_x", format!("{:.4}", minor_vec.x)),
ro("Minor axis vector Y", "minor_y", format!("{:.4}", minor_vec.y)), ro(t!("Minor axis vector Y").as_ref(), "minor_y", format!("{:.4}", minor_vec.y)),
ro("Minor axis vector Z", "minor_z", format!("{:.4}", minor_vec.z)), ro(t!("Minor axis vector Z").as_ref(), "minor_z", format!("{:.4}", minor_vec.z)),
ro("Area", "area", format!("{:.4}", props.area)), ro(t!("Area").as_ref(), "area", format!("{:.4}", props.area)),
ro("Start parameter", "start_param", format!("{:.4}", ell.start_parameter)), ro(t!("Start parameter").as_ref(), "start_param", format!("{:.4}", ell.start_parameter)),
ro("End parameter", "end_param", format!("{:.4}", ell.end_parameter)), ro(t!("End parameter").as_ref(), "end_param", format!("{:.4}", ell.end_parameter)),
ro("Length", "length", format!("{:.4}", props.perimeter)), ro(t!("Length").as_ref(), "length", format!("{:.4}", props.perimeter)),
ro("Normal X", "normal_x", format!("{:.4}", ell.normal.x)), ro(t!("Normal X").as_ref(), "normal_x", format!("{:.4}", ell.normal.x)),
ro("Normal Y", "normal_y", format!("{:.4}", ell.normal.y)), ro(t!("Normal Y").as_ref(), "normal_y", format!("{:.4}", ell.normal.y)),
ro("Normal Z", "normal_z", format!("{:.4}", ell.normal.z)), ro(t!("Normal Z").as_ref(), "normal_z", format!("{:.4}", ell.normal.z)),
], ],
}] }]
} }

View file

@ -3,6 +3,7 @@ use acadrust::entities::{
PointCloudData, PointCloudExData, RemoteTextData, SectionObjectData, PointCloudData, PointCloudExData, RemoteTextData, SectionObjectData,
}; };
use acadrust::types::{Handle, Transform, Vector3}; use acadrust::types::{Handle, Transform, Vector3};
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{ use crate::entities::common::{
@ -29,7 +30,7 @@ fn handle_text(handle: Handle) -> String {
} }
} }
fn text_prop(label: &'static str, field: &'static str, value: &str) -> Property { fn text_prop(label: &str, field: &'static str, value: &str) -> Property {
Property { Property {
label: label.into(), label: label.into(),
field, field,
@ -37,7 +38,7 @@ fn text_prop(label: &'static str, field: &'static str, value: &str) -> Property
} }
} }
fn bool_prop(label: &'static str, field: &'static str, value: bool) -> Property { fn bool_prop(label: &str, field: &'static str, value: bool) -> Property {
Property { Property {
label: label.into(), label: label.into(),
field, field,
@ -481,40 +482,36 @@ fn section_properties(data: &SectionObjectData) -> Vec<PropSection> {
.join("\n"); .join("\n");
vec![ vec![
PropSection { PropSection {
title: "Section Object".into(), title: t!("Section Object").into_owned(),
props: vec![ props: vec![
text_prop("Name", "ext_section_name", &data.name), text_prop(t!("Name").as_ref(), "ext_section_name", &data.name),
ro_prop("State", "ext_section_state", data.state.to_string()), ro_prop(t!("State").as_ref(), "ext_section_state", data.state.to_string()),
ro_prop("Flags", "ext_section_flags", data.flags.to_string()), ro_prop(t!("Flags").as_ref(), "ext_section_flags", data.flags.to_string()),
ro_prop( ro_prop(t!("Vertical Direction").as_ref(),
"Vertical Direction",
"ext_section_vertical", "ext_section_vertical",
vector_text(data.vertical_direction), vector_text(data.vertical_direction),
), ),
edit_prop("Top Height", "ext_section_top", data.top_height), edit_prop(t!("Top Height").as_ref(), "ext_section_top", data.top_height),
edit_prop("Bottom Height", "ext_section_bottom", data.bottom_height), edit_prop(t!("Bottom Height").as_ref(), "ext_section_bottom", data.bottom_height),
ro_prop( ro_prop(t!("Indicator Alpha").as_ref(),
"Indicator Alpha",
"ext_section_alpha", "ext_section_alpha",
data.indicator_alpha.to_string(), data.indicator_alpha.to_string(),
), ),
ro_prop( ro_prop(t!("Indicator Color").as_ref(),
"Indicator Color",
"ext_section_color", "ext_section_color",
format!("{:?}", data.indicator_color), format!("{:?}", data.indicator_color),
), ),
ro_prop( ro_prop(t!("Settings").as_ref(),
"Settings",
"ext_section_settings", "ext_section_settings",
handle_text(data.settings_handle), handle_text(data.settings_handle),
), ),
], ],
}, },
PropSection { PropSection {
title: "Section Vertices".into(), title: t!("Section Vertices").into_owned(),
props: vec![ props: vec![
ro_prop("Cutting Line", "ext_section_vertices", vertices), ro_prop(t!("Cutting Line").as_ref(), "ext_section_vertices", vertices),
ro_prop("Back Line", "ext_section_back_vertices", back_vertices), ro_prop(t!("Back Line").as_ref(), "ext_section_back_vertices", back_vertices),
], ],
}, },
] ]
@ -522,114 +519,102 @@ fn section_properties(data: &SectionObjectData) -> Vec<PropSection> {
fn arc_text_properties(data: &ArcAlignedTextData) -> Vec<PropSection> { fn arc_text_properties(data: &ArcAlignedTextData) -> Vec<PropSection> {
vec![PropSection { vec![PropSection {
title: "Arc-Aligned Text".into(), title: t!("Arc-Aligned Text").into_owned(),
props: vec![ props: vec![
text_prop("Text", "ext_arc_text", &data.text), text_prop(t!("Text").as_ref(), "ext_arc_text", &data.text),
text_prop("Font", "ext_arc_font", &data.font_name), text_prop(t!("Font").as_ref(), "ext_arc_font", &data.font_name),
text_prop("Big Font", "ext_arc_big_font", &data.big_font_name), text_prop(t!("Big Font").as_ref(), "ext_arc_big_font", &data.big_font_name),
text_prop("Style", "ext_arc_style", &data.style_name), text_prop(t!("Style").as_ref(), "ext_arc_style", &data.style_name),
ro_prop("Center", "ext_arc_center", vector_text(data.center)), ro_prop(t!("Center").as_ref(), "ext_arc_center", vector_text(data.center)),
edit_prop("Radius", "ext_arc_radius", data.radius), edit_prop(t!("Radius").as_ref(), "ext_arc_radius", data.radius),
edit_prop("X Scale", "ext_arc_xscale", data.x_scale), edit_prop(t!("X Scale").as_ref(), "ext_arc_xscale", data.x_scale),
edit_prop("Text Size", "ext_arc_size", data.text_size), edit_prop(t!("Text Size").as_ref(), "ext_arc_size", data.text_size),
edit_prop( edit_prop(t!("Character Spacing").as_ref(),
"Character Spacing",
"ext_arc_spacing", "ext_arc_spacing",
data.character_spacing, data.character_spacing,
), ),
edit_prop( edit_prop(t!("Offset From Arc").as_ref(),
"Offset From Arc",
"ext_arc_offset", "ext_arc_offset",
data.offset_from_arc, data.offset_from_arc,
), ),
edit_prop("Right Offset", "ext_arc_right", data.right_offset), edit_prop(t!("Right Offset").as_ref(), "ext_arc_right", data.right_offset),
edit_prop("Left Offset", "ext_arc_left", data.left_offset), edit_prop(t!("Left Offset").as_ref(), "ext_arc_left", data.left_offset),
edit_angle_prop( edit_angle_prop(t!("Start Angle").as_ref(),
"Start Angle",
"ext_arc_start", "ext_arc_start",
data.start_angle.to_degrees(), data.start_angle.to_degrees(),
), ),
edit_angle_prop("End Angle", "ext_arc_end", data.end_angle.to_degrees()), edit_angle_prop(t!("End Angle").as_ref(), "ext_arc_end", data.end_angle.to_degrees()),
bool_prop("Reverse", "ext_arc_reverse", data.reverse), bool_prop(t!("Reverse").as_ref(), "ext_arc_reverse", data.reverse),
ro_prop( ro_prop(t!("Text Direction").as_ref(),
"Text Direction",
"ext_arc_direction", "ext_arc_direction",
data.text_direction.to_string(), data.text_direction.to_string(),
), ),
ro_prop("Alignment", "ext_arc_alignment", data.alignment.to_string()), ro_prop(t!("Alignment").as_ref(), "ext_arc_alignment", data.alignment.to_string()),
ro_prop( ro_prop(t!("Text Position").as_ref(),
"Text Position",
"ext_arc_position", "ext_arc_position",
data.text_position.to_string(), data.text_position.to_string(),
), ),
bool_prop("Bold", "ext_arc_bold", data.bold), bool_prop(t!("Bold").as_ref(), "ext_arc_bold", data.bold),
bool_prop("Italic", "ext_arc_italic", data.italic), bool_prop(t!("Italic").as_ref(), "ext_arc_italic", data.italic),
bool_prop("Underlined", "ext_arc_underlined", data.underlined), bool_prop(t!("Underlined").as_ref(), "ext_arc_underlined", data.underlined),
ro_prop( ro_prop(t!("Character Set").as_ref(),
"Character Set",
"ext_arc_charset", "ext_arc_charset",
data.character_set.to_string(), data.character_set.to_string(),
), ),
ro_prop( ro_prop(t!("Pitch And Family").as_ref(),
"Pitch And Family",
"ext_arc_pitch", "ext_arc_pitch",
data.pitch_and_family.to_string(), data.pitch_and_family.to_string(),
), ),
bool_prop("SHX", "ext_arc_shx", data.is_shx), bool_prop(t!("SHX").as_ref(), "ext_arc_shx", data.is_shx),
ro_prop("Text Color", "ext_arc_color", data.text_color.to_string()), ro_prop(t!("Text Color").as_ref(), "ext_arc_color", data.text_color.to_string()),
ro_prop("Normal", "ext_arc_normal", vector_text(data.normal)), ro_prop(t!("Normal").as_ref(), "ext_arc_normal", vector_text(data.normal)),
bool_prop("Wizard Flag", "ext_arc_wizard", data.wizard_flag), bool_prop(t!("Wizard Flag").as_ref(), "ext_arc_wizard", data.wizard_flag),
ro_prop("Arc", "ext_arc_handle", handle_text(data.arc_handle)), ro_prop(t!("Arc").as_ref(), "ext_arc_handle", handle_text(data.arc_handle)),
], ],
}] }]
} }
fn remote_text_properties(data: &RemoteTextData) -> Vec<PropSection> { fn remote_text_properties(data: &RemoteTextData) -> Vec<PropSection> {
vec![PropSection { vec![PropSection {
title: "Remote Text".into(), title: t!("Remote Text").into_owned(),
props: vec![ props: vec![
text_prop("Text", "ext_rtext_text", &data.text), text_prop(t!("Text").as_ref(), "ext_rtext_text", &data.text),
ro_prop("Position", "ext_rtext_position", vector_text(data.position)), ro_prop(t!("Position").as_ref(), "ext_rtext_position", vector_text(data.position)),
ro_prop("Normal", "ext_rtext_normal", vector_text(data.normal)), ro_prop(t!("Normal").as_ref(), "ext_rtext_normal", vector_text(data.normal)),
edit_angle_prop( edit_angle_prop(t!("Rotation").as_ref(),
"Rotation",
"ext_rtext_rotation", "ext_rtext_rotation",
data.rotation.to_degrees(), data.rotation.to_degrees(),
), ),
edit_prop("Height", "ext_rtext_height", data.height), edit_prop(t!("Height").as_ref(), "ext_rtext_height", data.height),
text_prop("Style Name", "ext_rtext_style", &data.style_name), text_prop(t!("Style Name").as_ref(), "ext_rtext_style", &data.style_name),
ro_prop( ro_prop(t!("Style Handle").as_ref(),
"Style Handle",
"ext_rtext_style_handle", "ext_rtext_style_handle",
handle_text(data.style_handle), handle_text(data.style_handle),
), ),
ro_prop("Flags", "ext_rtext_flags", data.flags.to_string()), ro_prop(t!("Flags").as_ref(), "ext_rtext_flags", data.flags.to_string()),
], ],
}] }]
} }
fn geo_marker_properties(data: &GeoPositionMarkerData) -> Vec<PropSection> { fn geo_marker_properties(data: &GeoPositionMarkerData) -> Vec<PropSection> {
vec![PropSection { vec![PropSection {
title: "Geographic Position Marker".into(), title: t!("Geographic Position Marker").into_owned(),
props: vec![ props: vec![
ro_prop( ro_prop(t!("Class Version").as_ref(),
"Class Version",
"ext_geo_version", "ext_geo_version",
data.class_version.to_string(), data.class_version.to_string(),
), ),
ro_prop("Position", "ext_geo_position", vector_text(data.position)), ro_prop(t!("Position").as_ref(), "ext_geo_position", vector_text(data.position)),
edit_prop("Radius", "ext_geo_radius", data.radius), edit_prop(t!("Radius").as_ref(), "ext_geo_radius", data.radius),
text_prop("Notes", "ext_geo_notes", &data.notes), text_prop(t!("Notes").as_ref(), "ext_geo_notes", &data.notes),
edit_prop("Landing Gap", "ext_geo_gap", data.landing_gap), edit_prop(t!("Landing Gap").as_ref(), "ext_geo_gap", data.landing_gap),
bool_prop("Text Visible", "ext_geo_visible", data.mtext_visible), bool_prop(t!("Text Visible").as_ref(), "ext_geo_visible", data.mtext_visible),
ro_prop( ro_prop(t!("Text Alignment").as_ref(),
"Text Alignment",
"ext_geo_alignment", "ext_geo_alignment",
data.text_alignment.to_string(), data.text_alignment.to_string(),
), ),
bool_prop("Frame Text", "ext_geo_frame", data.enable_frame_text), bool_prop(t!("Frame Text").as_ref(), "ext_geo_frame", data.enable_frame_text),
ro_prop( ro_prop(t!("Embedded MText").as_ref(),
"Embedded MText",
"ext_geo_mtext", "ext_geo_mtext",
data.embedded_mtext data.embedded_mtext
.as_ref() .as_ref()
@ -660,91 +645,77 @@ fn point_cloud_properties(data: &PointCloudData) -> Vec<PropSection> {
.join("\n"); .join("\n");
vec![ vec![
PropSection { PropSection {
title: "Point Cloud".into(), title: t!("Point Cloud").into_owned(),
props: vec![ props: vec![
ro_prop( ro_prop(t!("Class Version").as_ref(),
"Class Version",
"ext_pc_version", "ext_pc_version",
data.class_version.to_string(), data.class_version.to_string(),
), ),
ro_prop("Origin", "ext_pc_origin", vector_text(data.origin)), ro_prop(t!("Origin").as_ref(), "ext_pc_origin", vector_text(data.origin)),
text_prop("Saved File", "ext_pc_file", &data.saved_filename), text_prop(t!("Saved File").as_ref(), "ext_pc_file", &data.saved_filename),
ro_prop( ro_prop(t!("Source Files").as_ref(),
"Source Files",
"ext_pc_sources", "ext_pc_sources",
data.source_files.join("\n"), data.source_files.join("\n"),
), ),
ro_prop("Extents Min", "ext_pc_min", vector_text(data.extents_min)), ro_prop(t!("Extents Min").as_ref(), "ext_pc_min", vector_text(data.extents_min)),
ro_prop("Extents Max", "ext_pc_max", vector_text(data.extents_max)), ro_prop(t!("Extents Max").as_ref(), "ext_pc_max", vector_text(data.extents_max)),
ro_prop( ro_prop(t!("Point Count").as_ref(),
"Point Count",
"ext_pc_count", "ext_pc_count",
data.point_count.to_string(), data.point_count.to_string(),
), ),
text_prop("UCS Name", "ext_pc_ucs", &data.ucs_name), text_prop(t!("UCS Name").as_ref(), "ext_pc_ucs", &data.ucs_name),
ro_prop( ro_prop(t!("UCS Origin").as_ref(),
"UCS Origin",
"ext_pc_ucs_origin", "ext_pc_ucs_origin",
vector_text(data.ucs_origin), vector_text(data.ucs_origin),
), ),
ro_prop( ro_prop(t!("UCS X").as_ref(),
"UCS X",
"ext_pc_ucs_x", "ext_pc_ucs_x",
vector_text(data.ucs_x_direction), vector_text(data.ucs_x_direction),
), ),
ro_prop( ro_prop(t!("UCS Y").as_ref(),
"UCS Y",
"ext_pc_ucs_y", "ext_pc_ucs_y",
vector_text(data.ucs_y_direction), vector_text(data.ucs_y_direction),
), ),
ro_prop( ro_prop(t!("UCS Z").as_ref(),
"UCS Z",
"ext_pc_ucs_z", "ext_pc_ucs_z",
vector_text(data.ucs_z_direction), vector_text(data.ucs_z_direction),
), ),
ro_prop( ro_prop(t!("Definition").as_ref(),
"Definition",
"ext_pc_definition", "ext_pc_definition",
handle_text(data.definition_handle), handle_text(data.definition_handle),
), ),
ro_prop( ro_prop(t!("Reactor").as_ref(),
"Reactor",
"ext_pc_reactor", "ext_pc_reactor",
handle_text(data.reactor_handle), handle_text(data.reactor_handle),
), ),
], ],
}, },
PropSection { PropSection {
title: "Point Cloud Display".into(), title: t!("Point Cloud Display").into_owned(),
props: vec![ props: vec![
bool_prop("Show Intensity", "ext_pc_show_intensity", data.show_intensity), bool_prop(t!("Show Intensity").as_ref(), "ext_pc_show_intensity", data.show_intensity),
ro_prop( ro_prop(t!("Intensity Scheme").as_ref(),
"Intensity Scheme",
"ext_pc_intensity_scheme", "ext_pc_intensity_scheme",
data.intensity_scheme.to_string(), data.intensity_scheme.to_string(),
), ),
edit_prop( edit_prop(t!("Minimum Intensity").as_ref(),
"Minimum Intensity",
"ext_pc_intensity_min", "ext_pc_intensity_min",
data.minimum_intensity, data.minimum_intensity,
), ),
edit_prop( edit_prop(t!("Maximum Intensity").as_ref(),
"Maximum Intensity",
"ext_pc_intensity_max", "ext_pc_intensity_max",
data.maximum_intensity, data.maximum_intensity,
), ),
edit_prop( edit_prop(t!("Low Threshold").as_ref(),
"Low Threshold",
"ext_pc_low_threshold", "ext_pc_low_threshold",
data.low_intensity_threshold, data.low_intensity_threshold,
), ),
edit_prop( edit_prop(t!("High Threshold").as_ref(),
"High Threshold",
"ext_pc_high_threshold", "ext_pc_high_threshold",
data.high_intensity_threshold, data.high_intensity_threshold,
), ),
bool_prop("Show Clipping", "ext_pc_show_clipping", data.show_clipping), bool_prop(t!("Show Clipping").as_ref(), "ext_pc_show_clipping", data.show_clipping),
ro_prop("Clippings", "ext_pc_clippings", clips), ro_prop(t!("Clippings").as_ref(), "ext_pc_clippings", clips),
], ],
}, },
] ]
@ -770,92 +741,77 @@ fn point_cloud_ex_properties(data: &PointCloudExData) -> Vec<PropSection> {
.join("\n"); .join("\n");
vec![ vec![
PropSection { PropSection {
title: "Point Cloud Ex".into(), title: t!("Point Cloud Ex").into_owned(),
props: vec![ props: vec![
ro_prop( ro_prop(t!("Class Version").as_ref(),
"Class Version",
"ext_pcx_version", "ext_pcx_version",
data.class_version.to_string(), data.class_version.to_string(),
), ),
text_prop("Name", "ext_pcx_name", &data.name), text_prop(t!("Name").as_ref(), "ext_pcx_name", &data.name),
ro_prop("Extents Min", "ext_pcx_min", vector_text(data.extents_min)), ro_prop(t!("Extents Min").as_ref(), "ext_pcx_min", vector_text(data.extents_min)),
ro_prop("Extents Max", "ext_pcx_max", vector_text(data.extents_max)), ro_prop(t!("Extents Max").as_ref(), "ext_pcx_max", vector_text(data.extents_max)),
ro_prop( ro_prop(t!("UCS Origin").as_ref(),
"UCS Origin",
"ext_pcx_ucs_origin", "ext_pcx_ucs_origin",
vector_text(data.ucs_origin), vector_text(data.ucs_origin),
), ),
ro_prop( ro_prop(t!("UCS X").as_ref(),
"UCS X",
"ext_pcx_ucs_x", "ext_pcx_ucs_x",
vector_text(data.ucs_x_direction), vector_text(data.ucs_x_direction),
), ),
ro_prop( ro_prop(t!("UCS Y").as_ref(),
"UCS Y",
"ext_pcx_ucs_y", "ext_pcx_ucs_y",
vector_text(data.ucs_y_direction), vector_text(data.ucs_y_direction),
), ),
ro_prop( ro_prop(t!("UCS Z").as_ref(),
"UCS Z",
"ext_pcx_ucs_z", "ext_pcx_ucs_z",
vector_text(data.ucs_z_direction), vector_text(data.ucs_z_direction),
), ),
bool_prop("Locked", "ext_pcx_locked", data.locked), bool_prop(t!("Locked").as_ref(), "ext_pcx_locked", data.locked),
ro_prop( ro_prop(t!("Definition").as_ref(),
"Definition",
"ext_pcx_definition", "ext_pcx_definition",
handle_text(data.definition_handle), handle_text(data.definition_handle),
), ),
ro_prop( ro_prop(t!("Reactor").as_ref(),
"Reactor",
"ext_pcx_reactor", "ext_pcx_reactor",
handle_text(data.reactor_handle), handle_text(data.reactor_handle),
), ),
], ],
}, },
PropSection { PropSection {
title: "Point Cloud Ex Display".into(), title: t!("Point Cloud Ex Display").into_owned(),
props: vec![ props: vec![
bool_prop( bool_prop(t!("Show Intensity").as_ref(),
"Show Intensity",
"ext_pcx_show_intensity", "ext_pcx_show_intensity",
data.show_intensity, data.show_intensity,
), ),
bool_prop("Show Cropping", "ext_pcx_show_cropping", data.show_cropping), bool_prop(t!("Show Cropping").as_ref(), "ext_pcx_show_cropping", data.show_cropping),
ro_prop( ro_prop(t!("Unknown Flags").as_ref(),
"Unknown Flags",
"ext_pcx_unknown", "ext_pcx_unknown",
format!("{}, {}", data.unknown_bl0, data.unknown_bl1), format!("{}, {}", data.unknown_bl0, data.unknown_bl1),
), ),
ro_prop( ro_prop(t!("Stylization Type").as_ref(),
"Stylization Type",
"ext_pcx_stylization", "ext_pcx_stylization",
data.stylization_type.to_string(), data.stylization_type.to_string(),
), ),
text_prop( text_prop(t!("Intensity Color Scheme").as_ref(),
"Intensity Color Scheme",
"ext_pcx_intensity_scheme", "ext_pcx_intensity_scheme",
&data.intensity_color_scheme, &data.intensity_color_scheme,
), ),
text_prop( text_prop(t!("Current Color Scheme").as_ref(),
"Current Color Scheme",
"ext_pcx_current_scheme", "ext_pcx_current_scheme",
&data.current_color_scheme, &data.current_color_scheme,
), ),
text_prop( text_prop(t!("Classification Scheme").as_ref(),
"Classification Scheme",
"ext_pcx_class_scheme", "ext_pcx_class_scheme",
&data.classification_color_scheme, &data.classification_color_scheme,
), ),
edit_prop("Elevation Min", "ext_pcx_elevation_min", data.elevation_min), edit_prop(t!("Elevation Min").as_ref(), "ext_pcx_elevation_min", data.elevation_min),
edit_prop("Elevation Max", "ext_pcx_elevation_max", data.elevation_max), edit_prop(t!("Elevation Max").as_ref(), "ext_pcx_elevation_max", data.elevation_max),
ro_prop( ro_prop(t!("Intensity Range").as_ref(),
"Intensity Range",
"ext_pcx_intensity_range", "ext_pcx_intensity_range",
format!("{}..{}", data.intensity_min, data.intensity_max), format!("{}..{}", data.intensity_min, data.intensity_max),
), ),
ro_prop( ro_prop(t!("Out Of Range Behavior").as_ref(),
"Out Of Range Behavior",
"ext_pcx_out_of_range", "ext_pcx_out_of_range",
format!( format!(
"intensity {}; elevation {}", "intensity {}; elevation {}",
@ -863,22 +819,19 @@ fn point_cloud_ex_properties(data: &PointCloudExData) -> Vec<PropSection> {
data.elevation_out_of_range_behavior data.elevation_out_of_range_behavior
), ),
), ),
bool_prop( bool_prop(t!("Fixed Elevation Range").as_ref(),
"Fixed Elevation Range",
"ext_pcx_fixed_range", "ext_pcx_fixed_range",
data.elevation_apply_to_fixed_range, data.elevation_apply_to_fixed_range,
), ),
bool_prop( bool_prop(t!("Intensity Gradient").as_ref(),
"Intensity Gradient",
"ext_pcx_intensity_gradient", "ext_pcx_intensity_gradient",
data.intensity_as_gradient, data.intensity_as_gradient,
), ),
bool_prop( bool_prop(t!("Elevation Gradient").as_ref(),
"Elevation Gradient",
"ext_pcx_elevation_gradient", "ext_pcx_elevation_gradient",
data.elevation_as_gradient, data.elevation_as_gradient,
), ),
ro_prop("Croppings", "ext_pcx_croppings", crops), ro_prop(t!("Croppings").as_ref(), "ext_pcx_croppings", crops),
], ],
}, },
] ]
@ -918,25 +871,23 @@ fn reference_properties(
fn properties(entity: &ExtendedEntity) -> Vec<PropSection> { fn properties(entity: &ExtendedEntity) -> Vec<PropSection> {
match &entity.data { match &entity.data {
ExtendedEntityData::Camera { view_handle } => vec![PropSection { ExtendedEntityData::Camera { view_handle } => vec![PropSection {
title: "Camera".into(), title: t!("Camera").into_owned(),
props: vec![ro_prop("View", "ext_camera_view", handle_text(*view_handle))], props: vec![ro_prop(t!("View").as_ref(), "ext_camera_view", handle_text(*view_handle))],
}], }],
ExtendedEntityData::SectionObject(data) => section_properties(data), ExtendedEntityData::SectionObject(data) => section_properties(data),
ExtendedEntityData::ArcAlignedText(data) => arc_text_properties(data), ExtendedEntityData::ArcAlignedText(data) => arc_text_properties(data),
ExtendedEntityData::RemoteText(data) => remote_text_properties(data), ExtendedEntityData::RemoteText(data) => remote_text_properties(data),
ExtendedEntityData::GeoPositionMarker(data) => geo_marker_properties(data), ExtendedEntityData::GeoPositionMarker(data) => geo_marker_properties(data),
ExtendedEntityData::CoordinationModel(data) => vec![PropSection { ExtendedEntityData::CoordinationModel(data) => vec![PropSection {
title: "Coordination Model".into(), title: t!("Coordination Model").into_owned(),
props: vec![ props: vec![
ro_prop("Flags", "ext_coord_flags", data.flags.to_string()), ro_prop(t!("Flags").as_ref(), "ext_coord_flags", data.flags.to_string()),
ro_prop( ro_prop(t!("Definition").as_ref(),
"Definition",
"ext_coord_definition", "ext_coord_definition",
handle_text(data.definition_handle), handle_text(data.definition_handle),
), ),
edit_prop("Unit Factor", "ext_coord_unit", data.unit_factor), edit_prop(t!("Unit Factor").as_ref(), "ext_coord_unit", data.unit_factor),
ro_prop( ro_prop(t!("Transform").as_ref(),
"Transform",
"ext_coord_transform", "ext_coord_transform",
data.transform data.transform
.chunks_exact(4) .chunks_exact(4)
@ -954,68 +905,60 @@ fn properties(entity: &ExtendedEntity) -> Vec<PropSection> {
ExtendedEntityData::PointCloud(data) => point_cloud_properties(data), ExtendedEntityData::PointCloud(data) => point_cloud_properties(data),
ExtendedEntityData::PointCloudEx(data) => point_cloud_ex_properties(data), ExtendedEntityData::PointCloudEx(data) => point_cloud_ex_properties(data),
ExtendedEntityData::Proxy(data) => vec![PropSection { ExtendedEntityData::Proxy(data) => vec![PropSection {
title: "Proxy Entity".into(), title: t!("Proxy Entity").into_owned(),
props: vec![ props: vec![
ro_prop("Proxy ID", "ext_proxy_id", data.proxy_id.to_string()), ro_prop(t!("Proxy ID").as_ref(), "ext_proxy_id", data.proxy_id.to_string()),
ro_prop("Class ID", "ext_proxy_class", data.class_id.to_string()), ro_prop(t!("Class ID").as_ref(), "ext_proxy_class", data.class_id.to_string()),
ro_prop("DXF Subclass", "ext_proxy_subclass", data.dxf_subclass.clone()), ro_prop(t!("DXF Subclass").as_ref(), "ext_proxy_subclass", data.dxf_subclass.clone()),
ro_prop("Version", "ext_proxy_version", data.version.to_string()), ro_prop(t!("Version").as_ref(), "ext_proxy_version", data.version.to_string()),
ro_prop( ro_prop(t!("DWG Version").as_ref(),
"DWG Version",
"ext_proxy_dwg_version", "ext_proxy_dwg_version",
format!("{}.{}", data.dwg_version, data.maintenance_version), format!("{}.{}", data.dwg_version, data.maintenance_version),
), ),
ro_prop("From DXF", "ext_proxy_from_dxf", data.from_dxf.to_string()), ro_prop(t!("From DXF").as_ref(), "ext_proxy_from_dxf", data.from_dxf.to_string()),
ro_prop( ro_prop(t!("Graphics").as_ref(),
"Graphics",
"ext_proxy_graphics", "ext_proxy_graphics",
format!("{} bits", data.graphics.bit_count), format!("{} bits", data.graphics.bit_count),
), ),
ro_prop( ro_prop(t!("Payload").as_ref(),
"Payload",
"ext_proxy_payload", "ext_proxy_payload",
format!("{} bits", data.payload.bit_count), format!("{} bits", data.payload.bit_count),
), ),
ro_prop( ro_prop(t!("Text Payload").as_ref(),
"Text Payload",
"ext_proxy_text_payload", "ext_proxy_text_payload",
format!("{} bits", data.text_payload.bit_count), format!("{} bits", data.text_payload.bit_count),
), ),
ro_prop( ro_prop(t!("Object References").as_ref(),
"Object References",
"ext_proxy_references", "ext_proxy_references",
reference_properties(&data.object_ids), reference_properties(&data.object_ids),
), ),
], ],
}], }],
ExtendedEntityData::OleFrame(data) => vec![PropSection { ExtendedEntityData::OleFrame(data) => vec![PropSection {
title: "OLE Frame".into(), title: t!("OLE Frame").into_owned(),
props: vec![ props: vec![
ro_prop("Flag", "ext_ole_flag", data.flag.to_string()), ro_prop(t!("Flag").as_ref(), "ext_ole_flag", data.flag.to_string()),
ro_prop("Mode", "ext_ole_mode", data.mode.to_string()), ro_prop(t!("Mode").as_ref(), "ext_ole_mode", data.mode.to_string()),
ro_prop( ro_prop(t!("Storage Size").as_ref(),
"Storage Size",
"ext_ole_size", "ext_ole_size",
format!("{} bytes", data.storage.encoded_len()), format!("{} bytes", data.storage.encoded_len()),
), ),
], ],
}], }],
ExtendedEntityData::LayoutPrintConfig(data) => vec![PropSection { ExtendedEntityData::LayoutPrintConfig(data) => vec![PropSection {
title: "Layout Print Configuration".into(), title: t!("Layout Print Configuration").into_owned(),
props: vec![ props: vec![
ro_prop( ro_prop(t!("Class Version").as_ref(),
"Class Version",
"ext_print_version", "ext_print_version",
data.class_version.to_string(), data.class_version.to_string(),
), ),
ro_prop("Flag", "ext_print_flag", data.flag.to_string()), ro_prop(t!("Flag").as_ref(), "ext_print_flag", data.flag.to_string()),
], ],
}], }],
ExtendedEntityData::Format(data) => vec![PropSection { ExtendedEntityData::Format(data) => vec![PropSection {
title: "Format".into(), title: t!("Format").into_owned(),
props: vec![ props: vec![
ro_prop( ro_prop(t!("DWG Payload").as_ref(),
"DWG Payload",
"ext_format_dwg", "ext_format_dwg",
format!( format!(
"{} bytes / {} handle bits", "{} bytes / {} handle bits",
@ -1023,54 +966,47 @@ fn properties(entity: &ExtendedEntity) -> Vec<PropSection> {
data.raw_dwg_handle_bits data.raw_dwg_handle_bits
), ),
), ),
ro_prop( ro_prop(t!("DWG Version").as_ref(),
"DWG Version",
"ext_format_version", "ext_format_version",
format!("{:?}", data.raw_dwg_version), format!("{:?}", data.raw_dwg_version),
), ),
ro_prop( ro_prop(t!("DXF Codes").as_ref(),
"DXF Codes",
"ext_format_dxf", "ext_format_dxf",
data.raw_dxf_codes.as_ref().map_or(0, Vec::len).to_string(), data.raw_dxf_codes.as_ref().map_or(0, Vec::len).to_string(),
), ),
], ],
}], }],
ExtendedEntityData::Legacy(data) => vec![PropSection { ExtendedEntityData::Legacy(data) => vec![PropSection {
title: "Legacy Entity".into(), title: t!("Legacy Entity").into_owned(),
props: vec![ro_prop("Data", "ext_legacy_data", format!("{data:#?}"))], props: vec![ro_prop(t!("Data").as_ref(), "ext_legacy_data", format!("{data:#?}"))],
}], }],
ExtendedEntityData::DynamicBlock(data) => vec![PropSection { ExtendedEntityData::DynamicBlock(data) => vec![PropSection {
title: "Dynamic Block Entity".into(), title: t!("Dynamic Block Entity").into_owned(),
props: vec![ props: vec![
ro_prop( ro_prop(t!("Class").as_ref(),
"Class",
"ext_dynamic_class", "ext_dynamic_class",
data.entity_dxf_name().unwrap_or("Helper"), data.entity_dxf_name().unwrap_or("Helper"),
), ),
ro_prop("Decoded Data", "ext_dynamic_data", format!("{data:#?}")), ro_prop(t!("Decoded Data").as_ref(), "ext_dynamic_data", format!("{data:#?}")),
], ],
}], }],
ExtendedEntityData::RegisteredClass(data) => vec![PropSection { ExtendedEntityData::RegisteredClass(data) => vec![PropSection {
title: "Registered Class Entity".into(), title: t!("Registered Class Entity").into_owned(),
props: vec![ props: vec![
ro_prop("DXF Name", "ext_registered_dxf", data.dxf_name.clone()), ro_prop(t!("DXF Name").as_ref(), "ext_registered_dxf", data.dxf_name.clone()),
ro_prop( ro_prop(t!("C++ Class").as_ref(),
"C++ Class",
"ext_registered_cpp", "ext_registered_cpp",
data.cpp_class_name.clone(), data.cpp_class_name.clone(),
), ),
ro_prop( ro_prop(t!("Properties").as_ref(),
"Properties",
"ext_registered_properties", "ext_registered_properties",
semantic_properties(&data.properties), semantic_properties(&data.properties),
), ),
ro_prop( ro_prop(t!("Payload").as_ref(),
"Payload",
"ext_registered_payload", "ext_registered_payload",
format!("{} bits", data.payload.bit_count), format!("{} bits", data.payload.bit_count),
), ),
ro_prop( ro_prop(t!("Object References").as_ref(),
"Object References",
"ext_registered_references", "ext_registered_references",
reference_properties(&data.object_ids), reference_properties(&data.object_ids),
), ),

View file

@ -1,5 +1,6 @@
use acadrust::entities::{BoundaryEdge, Hatch}; use acadrust::entities::{BoundaryEdge, Hatch};
use glam::Vec3; use glam::Vec3;
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{center_grip, circle_grip, edit_angle_prop as edit_angle, edit_prop as edit, parse_f64, ro_prop as ro}; use crate::entities::common::{center_grip, circle_grip, edit_angle_prop as edit_angle, edit_prop as edit, parse_f64, ro_prop as ro};
@ -264,10 +265,10 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
let centered = if g.shift.abs() < 1e-9 { "Yes" } else { "No" }; let centered = if g.shift.abs() < 1e-9 { "Yes" } else { "No" };
let mut sections = vec![ let mut sections = vec![
PropSection { PropSection {
title: "Pattern".into(), title: t!("Pattern").into_owned(),
props: vec![ props: vec![
Property { Property {
label: "Type".into(), label: t!("Type").into_owned(),
field: "fill_type", field: "fill_type",
value: PropValue::Choice { value: PropValue::Choice {
selected: grad_type.to_string(), selected: grad_type.to_string(),
@ -279,7 +280,7 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
crate::scene::model::hatch_model::GradientKind::from_name(&g.name); crate::scene::model::hatch_model::GradientKind::from_name(&g.name);
let _ = invert; let _ = invert;
Property { Property {
label: "Gradient type".into(), label: t!("Gradient type").into_owned(),
field: "gradient_type", field: "gradient_type",
value: PropValue::Choice { value: PropValue::Choice {
selected: kind.label().to_string(), selected: kind.label().to_string(),
@ -291,7 +292,7 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
} }
}, },
Property { Property {
label: "Invert".into(), label: t!("Invert").into_owned(),
field: "gradient_invert", field: "gradient_invert",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "gradient_invert", field: "gradient_invert",
@ -302,39 +303,38 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
}, },
}, },
Property { Property {
label: "Color 1".into(), label: t!("Color 1").into_owned(),
field: "gradient_color_1", field: "gradient_color_1",
value: PropValue::ColorChoice(grad_c1), value: PropValue::ColorChoice(grad_c1),
}, },
Property { Property {
label: "Color 2".into(), label: t!("Color 2").into_owned(),
field: "gradient_color_2", field: "gradient_color_2",
value: PropValue::ColorChoice(grad_c2), value: PropValue::ColorChoice(grad_c2),
}, },
edit_angle("Angle", "pattern_angle", g.angle.to_degrees()), edit_angle(t!("Angle").as_ref(), "pattern_angle", g.angle.to_degrees()),
ro("Centered", "gradient_centered", centered), ro(t!("Centered").as_ref(), "gradient_centered", centered),
], ],
}, },
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Elevation", "elevation", h.elevation), edit(t!("Elevation").as_ref(), "elevation", h.elevation),
ro("Area", "area", format!("{:.4}", area)), ro(t!("Area").as_ref(), "area", format!("{:.4}", area)),
ro("Cumulative area", "cumulative_area", format!("{:.4}", area)), ro(t!("Cumulative area").as_ref(), "cumulative_area", format!("{:.4}", area)),
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
ro( ro(t!("Associative").as_ref(),
"Associative",
"associative", "associative",
if h.is_associative { "Yes" } else { "No" }, if h.is_associative { "Yes" } else { "No" },
), ),
ro("Annotative", "annotative", String::new()), ro(t!("Annotative").as_ref(), "annotative", String::new()),
ro("Island detection style", "style", style), ro(t!("Island detection style").as_ref(), "style", style),
Property { Property {
label: "Background".into(), label: t!("Background").into_owned(),
field: "bg_enabled", field: "bg_enabled",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "bg_enabled", field: "bg_enabled",
@ -347,7 +347,7 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
if bg_on { if bg_on {
if let Some(sec) = sections.last_mut() { if let Some(sec) = sections.last_mut() {
sec.props.push(Property { sec.props.push(Property {
label: "Background color".into(), label: t!("Background color").into_owned(),
field: "background_color", field: "background_color",
value: PropValue::ColorChoice(bg_col), value: PropValue::ColorChoice(bg_col),
}); });
@ -360,7 +360,7 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
// ── Hatch (pattern / solid) ──────────────────────────────────────────── // ── Hatch (pattern / solid) ────────────────────────────────────────────
// "Type" = pattern definition source (Predefined / User Defined / Custom). // "Type" = pattern definition source (Predefined / User Defined / Custom).
let type_row = Property { let type_row = Property {
label: "Type".into(), label: t!("Type").into_owned(),
field: "pattern_type_label", field: "pattern_type_label",
value: PropValue::Choice { value: PropValue::Choice {
selected: pattern_type.to_string(), selected: pattern_type.to_string(),
@ -368,12 +368,12 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
}, },
}; };
let pattern_name_row = Property { let pattern_name_row = Property {
label: "Pattern name".into(), label: t!("Pattern name").into_owned(),
field: "pattern_name", field: "pattern_name",
value: PropValue::HatchPatternChoice(h.pattern.name.clone()), value: PropValue::HatchPatternChoice(h.pattern.name.clone()),
}; };
let associative_row = Property { let associative_row = Property {
label: "Associative".into(), label: t!("Associative").into_owned(),
field: "associative", field: "associative",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "associative", field: "associative",
@ -381,7 +381,7 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
}, },
}; };
let double_row = Property { let double_row = Property {
label: "Double".into(), label: t!("Double").into_owned(),
field: "double", field: "double",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "double", field: "double",
@ -389,15 +389,14 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
}, },
}; };
let island_row = Property { let island_row = Property {
label: "Island detection style".into(), label: t!("Island detection style").into_owned(),
field: "style", field: "style",
value: PropValue::Choice { value: PropValue::Choice {
selected: style.to_string(), selected: style.to_string(),
options: vec!["Normal".into(), "Outer".into(), "Ignore".into()], options: vec!["Normal".into(), "Outer".into(), "Ignore".into()],
}, },
}; };
let spacing_row = edit( let spacing_row = edit(t!("Spacing").as_ref(),
"Spacing",
"spacing", "spacing",
h.pattern h.pattern
.lines .lines
@ -415,22 +414,22 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
let mut sections = vec![ let mut sections = vec![
PropSection { PropSection {
title: "Pattern".into(), title: t!("Pattern").into_owned(),
props: vec![ props: vec![
type_row, type_row,
pattern_name_row, pattern_name_row,
ro("Annotative", "annotative", String::new()), ro(t!("Annotative").as_ref(), "annotative", String::new()),
edit_angle("Angle", "pattern_angle", h.pattern_angle.to_degrees()), edit_angle(t!("Angle").as_ref(), "pattern_angle", h.pattern_angle.to_degrees()),
edit("Scale", "pattern_scale", h.pattern_scale), edit(t!("Scale").as_ref(), "pattern_scale", h.pattern_scale),
edit("Origin X", "origin_x", origin_x), edit(t!("Origin X").as_ref(), "origin_x", origin_x),
edit("Origin Y", "origin_y", origin_y), edit(t!("Origin Y").as_ref(), "origin_y", origin_y),
spacing_row, spacing_row,
ro("ISO pen width", "iso_pen_width", String::new()), ro(t!("ISO pen width").as_ref(), "iso_pen_width", String::new()),
double_row, double_row,
associative_row, associative_row,
island_row, island_row,
Property { Property {
label: "Background".into(), label: t!("Background").into_owned(),
field: "bg_enabled", field: "bg_enabled",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "bg_enabled", field: "bg_enabled",
@ -440,18 +439,18 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
], ],
}, },
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Elevation", "elevation", h.elevation), edit(t!("Elevation").as_ref(), "elevation", h.elevation),
ro("Area", "area", format!("{:.4}", area)), ro(t!("Area").as_ref(), "area", format!("{:.4}", area)),
ro("Cumulative area", "cumulative_area", format!("{:.4}", area)), ro(t!("Cumulative area").as_ref(), "cumulative_area", format!("{:.4}", area)),
], ],
}, },
]; ];
if bg_on { if bg_on {
if let Some(sec) = sections.first_mut() { if let Some(sec) = sections.first_mut() {
sec.props.push(Property { sec.props.push(Property {
label: "Background color".into(), label: t!("Background color").into_owned(),
field: "background_color", field: "background_color",
value: PropValue::ColorChoice(bg_col), value: PropValue::ColorChoice(bg_col),
}); });

View file

@ -7,6 +7,7 @@
// as the Geometry property group. // as the Geometry property group.
use acadrust::entities::{Helix, HelixConstraint}; use acadrust::entities::{Helix, HelixConstraint};
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::ro_prop as ro; use crate::entities::common::ro_prop as ro;
@ -70,20 +71,20 @@ fn properties(helix: &Helix) -> Vec<PropSection> {
}; };
vec![PropSection { vec![PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
ro("Base point X", "base_x", format!("{:.4}", bp.x)), ro(t!("Base point X").as_ref(), "base_x", format!("{:.4}", bp.x)),
ro("Base point Y", "base_y", format!("{:.4}", bp.y)), ro(t!("Base point Y").as_ref(), "base_y", format!("{:.4}", bp.y)),
ro("Base point Z", "base_z", format!("{:.4}", bp.z)), ro(t!("Base point Z").as_ref(), "base_z", format!("{:.4}", bp.z)),
ro("Number of turns", "num_turns", format!("{turns:.4}")), ro(t!("Number of turns").as_ref(), "num_turns", format!("{turns:.4}")),
ro("Turn height", "turn_height", format!("{turn_height:.4}")), ro(t!("Turn height").as_ref(), "turn_height", format!("{turn_height:.4}")),
ro("Turns", "turns", format!("{turns:.4}")), ro(t!("Turns").as_ref(), "turns", format!("{turns:.4}")),
ro("Height", "height", format!("{height:.4}")), ro(t!("Height").as_ref(), "height", format!("{height:.4}")),
ro("Base radius", "base_radius", format!("{base_radius:.4}")), ro(t!("Base radius").as_ref(), "base_radius", format!("{base_radius:.4}")),
ro("Top radius", "top_radius", format!("{top_radius:.4}")), ro(t!("Top radius").as_ref(), "top_radius", format!("{top_radius:.4}")),
ro("Turn slope", "turn_slope", format!("{turn_slope:.4}")), ro(t!("Turn slope").as_ref(), "turn_slope", format!("{turn_slope:.4}")),
ro("Twist", "twist", twist), ro(t!("Twist").as_ref(), "twist", twist),
ro("Constrain", "constrain", constrain), ro(t!("Constrain").as_ref(), "constrain", constrain),
], ],
}] }]
} }

View file

@ -10,6 +10,7 @@ use crate::entities::traits::TruckConvertible;
use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject}; use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Property}; use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Property};
use crate::scene::model::wire_model::TangentGeom; use crate::scene::model::wire_model::TangentGeom;
use crate::t;
// ── TruckConvertible (used for snap/grip key-vertices) ───────────────────── // ── TruckConvertible (used for snap/grip key-vertices) ─────────────────────
@ -219,27 +220,27 @@ fn properties(leader: &Leader) -> Vec<PropSection> {
// Geometry sits right after General — a leader owns editable path vertices, // Geometry sits right after General — a leader owns editable path vertices,
// navigated one at a time by the Current Vertex spinner. // navigated one at a time by the Current Vertex spinner.
let mut geometry = vec![stepper("Current Vertex", "current_vertex", vertex_label)]; let mut geometry = vec![stepper(t!("Current Vertex").as_ref(), "current_vertex", vertex_label)];
if let Some(v) = leader.vertices.get(vi) { if let Some(v) = leader.vertices.get(vi) {
geometry.push(edit("Vertex X", "vertex_x", v.x)); geometry.push(edit(t!("Vertex X").as_ref(), "vertex_x", v.x));
geometry.push(edit("Vertex Y", "vertex_y", v.y)); geometry.push(edit(t!("Vertex Y").as_ref(), "vertex_y", v.y));
geometry.push(edit("Vertex Z", "vertex_z", v.z)); geometry.push(edit(t!("Vertex Z").as_ref(), "vertex_z", v.z));
} else { } else {
geometry.push(ro("Vertex X", "vertex_x", String::new())); geometry.push(ro(t!("Vertex X").as_ref(), "vertex_x", String::new()));
geometry.push(ro("Vertex Y", "vertex_y", String::new())); geometry.push(ro(t!("Vertex Y").as_ref(), "vertex_y", String::new()));
geometry.push(ro("Vertex Z", "vertex_z", String::new())); geometry.push(ro(t!("Vertex Z").as_ref(), "vertex_z", String::new()));
} }
// Misc: Dim style (upgraded to a dropdown by the panel builder), the // Misc: Dim style (upgraded to a dropdown by the panel builder), the
// combined path/arrow Type, and annotative state (from the dim style). // combined path/arrow Type, and annotative state (from the dim style).
let misc = vec![ let misc = vec![
Property { Property {
label: "Dim style".into(), label: t!("Dim style").into_owned(),
field: "dimension_style", field: "dimension_style",
value: PropValue::EditText(leader.dimension_style.clone()), value: PropValue::EditText(leader.dimension_style.clone()),
}, },
choice_prop( choice_prop(
"Type", t!("Type").as_ref(),
"leader_type", "leader_type",
leader_type_str(&leader.path_type, leader.arrow_enabled), leader_type_str(&leader.path_type, leader.arrow_enabled),
&[ &[
@ -249,42 +250,42 @@ fn properties(leader: &Leader) -> Vec<PropSection> {
"Spline without arrow", "Spline without arrow",
], ],
), ),
ro("Annotative", "annotative", "No"), ro(t!("Annotative").as_ref(), "annotative", "No"),
]; ];
// Lines & Arrows / Text / Fit are dimension-style-derived; the panel builder // Lines & Arrows / Text / Fit are dimension-style-derived; the panel builder
// resolves leader.dimension_style and fills these values from the DimStyle. // resolves leader.dimension_style and fills these values from the DimStyle.
let lines_arrows = vec![ let lines_arrows = vec![
ro("Arrow", "arrow_block", "Closed filled"), ro(t!("Arrow").as_ref(), "arrow_block", "Closed filled"),
ro("Arrow size", "arrow_size", String::new()), ro(t!("Arrow size").as_ref(), "arrow_size", String::new()),
ro("Dim line lineweight", "dim_line_lw", "ByLayer"), ro(t!("Dim line lineweight").as_ref(), "dim_line_lw", "ByLayer"),
ro("Dim line color", "dim_line_color", "ByLayer"), ro(t!("Dim line color").as_ref(), "dim_line_color", "ByLayer"),
]; ];
let text = vec![ let text = vec![
ro("Text offset", "text_offset", String::new()), ro(t!("Text offset").as_ref(), "text_offset", String::new()),
ro("Text pos vert", "text_pos_vert", String::new()), ro(t!("Text pos vert").as_ref(), "text_pos_vert", String::new()),
]; ];
let fit = vec![ro("Dim scale overall", "dim_scale_overall", String::new())]; let fit = vec![ro(t!("Dim scale overall").as_ref(), "dim_scale_overall", String::new())];
vec![ vec![
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: geometry, props: geometry,
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: misc, props: misc,
}, },
PropSection { PropSection {
title: "Lines & Arrows".into(), title: t!("Lines & Arrows").into_owned(),
props: lines_arrows, props: lines_arrows,
}, },
PropSection { PropSection {
title: "Text".into(), title: t!("Text").into_owned(),
props: text, props: text,
}, },
PropSection { PropSection {
title: "Fit".into(), title: t!("Fit").into_owned(),
props: fit, props: fit,
}, },
] ]

View file

@ -11,6 +11,7 @@
use acadrust::entities::Light; use acadrust::entities::Light;
use acadrust::EntityType; use acadrust::EntityType;
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{ use crate::entities::common::{
@ -262,27 +263,27 @@ impl PropertyEditable for Light {
_ => "Point", _ => "Point",
}; };
let mut props = vec![ let mut props = vec![
ro_prop("Name", "li_name", self.name.clone()), ro_prop(t!("Name").as_ref(), "li_name", self.name.clone()),
ro_prop("Type", "li_type", kind), ro_prop(t!("Type").as_ref(), "li_type", kind),
edit("Position X", "li_px", self.position.x), edit(t!("Position X").as_ref(), "li_px", self.position.x),
edit("Position Y", "li_py", self.position.y), edit(t!("Position Y").as_ref(), "li_py", self.position.y),
edit("Position Z", "li_pz", self.position.z), edit(t!("Position Z").as_ref(), "li_pz", self.position.z),
]; ];
if self.light_type != 2 { if self.light_type != 2 {
props.push(edit("Target X", "li_tx", self.target.x)); props.push(edit(t!("Target X").as_ref(), "li_tx", self.target.x));
props.push(edit("Target Y", "li_ty", self.target.y)); props.push(edit(t!("Target Y").as_ref(), "li_ty", self.target.y));
props.push(edit("Target Z", "li_tz", self.target.z)); props.push(edit(t!("Target Z").as_ref(), "li_tz", self.target.z));
} }
let mut sections = vec![ let mut sections = vec![
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props, props,
}, },
PropSection { PropSection {
title: "Light".into(), title: t!("Light").into_owned(),
props: vec![ props: vec![
Property { Property {
label: "On".into(), label: t!("On").into_owned(),
field: "li_status", field: "li_status",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "li_status", field: "li_status",
@ -290,17 +291,17 @@ impl PropertyEditable for Light {
}, },
}, },
Property { Property {
label: "Plot Glyph".into(), label: t!("Plot Glyph").into_owned(),
field: "li_plot_glyph", field: "li_plot_glyph",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "li_plot_glyph", field: "li_plot_glyph",
value: self.plot_glyph, value: self.plot_glyph,
}, },
}, },
ro_prop("Color", "li_color", format!("{:?}", self.light_color)), ro_prop(t!("Color").as_ref(), "li_color", format!("{:?}", self.light_color)),
edit("Intensity", "li_intensity", self.intensity), edit(t!("Intensity").as_ref(), "li_intensity", self.intensity),
Property { Property {
label: "Attenuation".into(), label: t!("Attenuation").into_owned(),
field: "li_attenuation", field: "li_attenuation",
value: PropValue::Choice { value: PropValue::Choice {
selected: match self.attenuation_type { selected: match self.attenuation_type {
@ -317,42 +318,39 @@ impl PropertyEditable for Light {
}, },
}, },
Property { Property {
label: "Use Limits".into(), label: t!("Use Limits").into_owned(),
field: "li_use_limits", field: "li_use_limits",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "li_use_limits", field: "li_use_limits",
value: self.use_attenuation_limits, value: self.use_attenuation_limits,
}, },
}, },
edit("Start Limit", "li_start", self.attenuation_start_limit), edit(t!("Start Limit").as_ref(), "li_start", self.attenuation_start_limit),
edit("End Limit", "li_end", self.attenuation_end_limit), edit(t!("End Limit").as_ref(), "li_end", self.attenuation_end_limit),
edit_angle_prop( edit_angle_prop(t!("Hotspot Angle").as_ref(),
"Hotspot Angle",
"li_hotspot", "li_hotspot",
self.hotspot_angle.to_degrees(), self.hotspot_angle.to_degrees(),
), ),
edit_angle_prop( edit_angle_prop(t!("Falloff Angle").as_ref(),
"Falloff Angle",
"li_falloff", "li_falloff",
self.falloff_angle.to_degrees(), self.falloff_angle.to_degrees(),
), ),
], ],
}, },
PropSection { PropSection {
title: "Shadows".into(), title: t!("Shadows").into_owned(),
props: vec![ props: vec![
Property { Property {
label: "Cast Shadows".into(), label: t!("Cast Shadows").into_owned(),
field: "li_shadows", field: "li_shadows",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "li_shadows", field: "li_shadows",
value: self.cast_shadows, value: self.cast_shadows,
}, },
}, },
ro_prop("Type", "li_shadow_type", self.shadow_type.to_string()), ro_prop(t!("Type").as_ref(), "li_shadow_type", self.shadow_type.to_string()),
ro_prop("Map Size", "li_shadow_size", self.shadow_map_size.to_string()), ro_prop(t!("Map Size").as_ref(), "li_shadow_size", self.shadow_map_size.to_string()),
ro_prop( ro_prop(t!("Softness").as_ref(),
"Softness",
"li_shadow_softness", "li_shadow_softness",
self.shadow_map_softness.to_string(), self.shadow_map_softness.to_string(),
), ),
@ -361,36 +359,30 @@ impl PropertyEditable for Light {
]; ];
if let Some(photo) = &self.photometric_data { if let Some(photo) = &self.photometric_data {
sections.push(PropSection { sections.push(PropSection {
title: "Photometric".into(), title: t!("Photometric").into_owned(),
props: vec![ props: vec![
ro_prop("Web File", "li_web_file", photo.web_file.clone()), ro_prop(t!("Web File").as_ref(), "li_web_file", photo.web_file.clone()),
ro_prop( ro_prop(t!("Physical Method").as_ref(),
"Physical Method",
"li_physical_method", "li_physical_method",
photo.physical_intensity_method.to_string(), photo.physical_intensity_method.to_string(),
), ),
edit( edit(t!("Physical Intensity").as_ref(),
"Physical Intensity",
"li_physical_intensity", "li_physical_intensity",
photo.physical_intensity, photo.physical_intensity,
), ),
edit( edit(t!("Illuminance Distance").as_ref(),
"Illuminance Distance",
"li_illuminance_distance", "li_illuminance_distance",
photo.illuminance_distance, photo.illuminance_distance,
), ),
edit( edit(t!("Color Temperature").as_ref(),
"Color Temperature",
"li_color_temperature", "li_color_temperature",
photo.lamp_color_temperature, photo.lamp_color_temperature,
), ),
ro_prop( ro_prop(t!("Lamp Preset").as_ref(),
"Lamp Preset",
"li_lamp_preset", "li_lamp_preset",
photo.lamp_color_preset.to_string(), photo.lamp_color_preset.to_string(),
), ),
ro_prop( ro_prop(t!("Shape").as_ref(),
"Shape",
"li_shape", "li_shape",
format!( format!(
"{} · {:.6} × {:.6} · radius {:.6}", "{} · {:.6} × {:.6} · radius {:.6}",
@ -400,8 +392,7 @@ impl PropertyEditable for Light {
photo.extended_light_radius photo.extended_light_radius
), ),
), ),
ro_prop( ro_prop(t!("Web").as_ref(),
"Web",
"li_web", "li_web",
format!( format!(
"type {}; symmetry {}; flux {:.6}; angles {:?}; rotation {:.6},{:.6},{:.6}", "type {}; symmetry {}; flux {:.6}; angles {:?}; rotation {:.6},{:.6},{:.6}",

View file

@ -1,4 +1,5 @@
use acadrust::{entities::Line, Entity}; use acadrust::{entities::Line, Entity};
use crate::t;
use truck_modeling::{builder, Point3}; use truck_modeling::{builder, Point3};
use crate::command::EntityTransform; use crate::command::EntityTransform;
@ -81,19 +82,19 @@ fn properties(line: &Line) -> Vec<PropSection> {
let dz = line.end.z - line.start.z; let dz = line.end.z - line.start.z;
let angle = dy.atan2(dx).to_degrees().rem_euclid(360.0); let angle = dy.atan2(dx).to_degrees().rem_euclid(360.0);
vec![PropSection { vec![PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Start X", "start_x", line.start.x), edit(t!("Start X").as_ref(), "start_x", line.start.x),
edit("Start Y", "start_y", line.start.y), edit(t!("Start Y").as_ref(), "start_y", line.start.y),
edit("Start Z", "start_z", line.start.z), edit(t!("Start Z").as_ref(), "start_z", line.start.z),
edit("End X", "end_x", line.end.x), edit(t!("End X").as_ref(), "end_x", line.end.x),
edit("End Y", "end_y", line.end.y), edit(t!("End Y").as_ref(), "end_y", line.end.y),
edit("End Z", "end_z", line.end.z), edit(t!("End Z").as_ref(), "end_z", line.end.z),
ro("Delta X", "delta_x", format!("{dx:.4}")), ro(t!("Delta X").as_ref(), "delta_x", format!("{dx:.4}")),
ro("Delta Y", "delta_y", format!("{dy:.4}")), ro(t!("Delta Y").as_ref(), "delta_y", format!("{dy:.4}")),
ro("Delta Z", "delta_z", format!("{dz:.4}")), ro(t!("Delta Z").as_ref(), "delta_z", format!("{dz:.4}")),
ro("Length", "length", format!("{:.4}", line.length())), ro(t!("Length").as_ref(), "length", format!("{:.4}", line.length())),
ro("Angle", "angle", format!("{angle:.2}")), ro(t!("Angle").as_ref(), "angle", format!("{angle:.2}")),
], ],
}] }]
} }

View file

@ -1,4 +1,5 @@
use acadrust::entities::{LwPolyline, LwVertex}; use acadrust::entities::{LwPolyline, LwVertex};
use crate::t;
use truck_modeling::{builder, Edge, Point3, Wire}; use truck_modeling::{builder, Edge, Point3, Wire};
use crate::command::EntityTransform; use crate::command::EntityTransform;
@ -616,24 +617,24 @@ fn properties(pline: &LwPolyline) -> Vec<PropSection> {
}; };
vec![ vec![
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
stepper("Current Vertex", "current_vertex", vertex_label), stepper(t!("Current Vertex").as_ref(), "current_vertex", vertex_label),
edit("Vertex X", "vertex_x", vx), edit(t!("Vertex X").as_ref(), "vertex_x", vx),
edit("Vertex Y", "vertex_y", vy), edit(t!("Vertex Y").as_ref(), "vertex_y", vy),
edit("Start segment width", "start_width", start_w), edit(t!("Start segment width").as_ref(), "start_width", start_w),
edit("End segment width", "end_width", end_w), edit(t!("End segment width").as_ref(), "end_width", end_w),
edit("Global width", "global_width", pline.constant_width), edit(t!("Global width").as_ref(), "global_width", pline.constant_width),
edit("Elevation", "elevation", pline.elevation), edit(t!("Elevation").as_ref(), "elevation", pline.elevation),
ro("Area", "area", format!("{:.4}", mp.area)), ro(t!("Area").as_ref(), "area", format!("{:.4}", mp.area)),
ro("Length", "length", format!("{:.4}", mp.perimeter)), ro(t!("Length").as_ref(), "length", format!("{:.4}", mp.perimeter)),
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
Property { Property {
label: "Closed".into(), label: t!("Closed").into_owned(),
field: "closed", field: "closed",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "closed", field: "closed",
@ -641,7 +642,7 @@ fn properties(pline: &LwPolyline) -> Vec<PropSection> {
}, },
}, },
Property { Property {
label: "Linetype generation".into(), label: t!("Linetype generation").into_owned(),
field: "plinegen", field: "plinegen",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "plinegen", field: "plinegen",

View file

@ -1,5 +1,6 @@
use acadrust::entities::{mesh::Mesh, polygon_mesh::PolygonMesh, Face3D, PolyfaceMesh}; use acadrust::entities::{mesh::Mesh, polygon_mesh::PolygonMesh, Face3D, PolyfaceMesh};
use glam::Vec3; use glam::Vec3;
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{parse_f64, ro_prop as ro, square_grip}; use crate::entities::common::{parse_f64, ro_prop as ro, square_grip};
@ -249,25 +250,25 @@ impl PropertyEditable for Face3D {
let inv = self.invisible_edges; let inv = self.invisible_edges;
let edge = |hidden: bool| if hidden { "Invisible" } else { "Visible" }; let edge = |hidden: bool| if hidden { "Invisible" } else { "Visible" };
vec![PropSection { vec![PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
ro("Current vertex", "f3_current", String::new()), ro(t!("Current vertex").as_ref(), "f3_current", String::new()),
edit("Vertex 1 X", "f3_p1x", self.first_corner.x), edit(t!("Vertex 1 X").as_ref(), "f3_p1x", self.first_corner.x),
edit("Vertex 1 Y", "f3_p1y", self.first_corner.y), edit(t!("Vertex 1 Y").as_ref(), "f3_p1y", self.first_corner.y),
edit("Vertex 1 Z", "f3_p1z", self.first_corner.z), edit(t!("Vertex 1 Z").as_ref(), "f3_p1z", self.first_corner.z),
edit("Vertex 2 X", "f3_p2x", self.second_corner.x), edit(t!("Vertex 2 X").as_ref(), "f3_p2x", self.second_corner.x),
edit("Vertex 2 Y", "f3_p2y", self.second_corner.y), edit(t!("Vertex 2 Y").as_ref(), "f3_p2y", self.second_corner.y),
edit("Vertex 2 Z", "f3_p2z", self.second_corner.z), edit(t!("Vertex 2 Z").as_ref(), "f3_p2z", self.second_corner.z),
edit("Vertex 3 X", "f3_p3x", self.third_corner.x), edit(t!("Vertex 3 X").as_ref(), "f3_p3x", self.third_corner.x),
edit("Vertex 3 Y", "f3_p3y", self.third_corner.y), edit(t!("Vertex 3 Y").as_ref(), "f3_p3y", self.third_corner.y),
edit("Vertex 3 Z", "f3_p3z", self.third_corner.z), edit(t!("Vertex 3 Z").as_ref(), "f3_p3z", self.third_corner.z),
edit("Vertex 4 X", "f3_p4x", self.fourth_corner.x), edit(t!("Vertex 4 X").as_ref(), "f3_p4x", self.fourth_corner.x),
edit("Vertex 4 Y", "f3_p4y", self.fourth_corner.y), edit(t!("Vertex 4 Y").as_ref(), "f3_p4y", self.fourth_corner.y),
edit("Vertex 4 Z", "f3_p4z", self.fourth_corner.z), edit(t!("Vertex 4 Z").as_ref(), "f3_p4z", self.fourth_corner.z),
ro("Edge 1", "f3_edge1", edge(inv.is_first_invisible())), ro(t!("Edge 1").as_ref(), "f3_edge1", edge(inv.is_first_invisible())),
ro("Edge 2", "f3_edge2", edge(inv.is_second_invisible())), ro(t!("Edge 2").as_ref(), "f3_edge2", edge(inv.is_second_invisible())),
ro("Edge 3", "f3_edge3", edge(inv.is_third_invisible())), ro(t!("Edge 3").as_ref(), "f3_edge3", edge(inv.is_third_invisible())),
ro("Edge 4", "f3_edge4", edge(inv.is_fourth_invisible())), ro(t!("Edge 4").as_ref(), "f3_edge4", edge(inv.is_fourth_invisible())),
], ],
}] }]
} }
@ -380,37 +381,34 @@ impl PropertyEditable for PolygonMesh {
let face_count = cells_m * cells_n; let face_count = cells_m * cells_n;
vec![ vec![
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
ro("Vertex", "pm_vertex", String::new()), ro(t!("Vertex").as_ref(), "pm_vertex", String::new()),
ro( ro(t!("Vertex X").as_ref(),
"Vertex X",
"pm_vx", "pm_vx",
first.map(|v| format!("{:.4}", v.location.x)).unwrap_or_default(), first.map(|v| format!("{:.4}", v.location.x)).unwrap_or_default(),
), ),
ro( ro(t!("Vertex Y").as_ref(),
"Vertex Y",
"pm_vy", "pm_vy",
first.map(|v| format!("{:.4}", v.location.y)).unwrap_or_default(), first.map(|v| format!("{:.4}", v.location.y)).unwrap_or_default(),
), ),
ro( ro(t!("Vertex Z").as_ref(),
"Vertex Z",
"pm_vz", "pm_vz",
first.map(|v| format!("{:.4}", v.location.z)).unwrap_or_default(), first.map(|v| format!("{:.4}", v.location.z)).unwrap_or_default(),
), ),
ro("M vertex count", "pm_m", self.m_vertex_count.to_string()), ro(t!("M vertex count").as_ref(), "pm_m", self.m_vertex_count.to_string()),
ro("N vertex count", "pm_n", self.n_vertex_count.to_string()), ro(t!("N vertex count").as_ref(), "pm_n", self.n_vertex_count.to_string()),
ro("M closed", "pm_closed_m", yesno(self.is_closed_m())), ro(t!("M closed").as_ref(), "pm_closed_m", yesno(self.is_closed_m())),
ro("N closed", "pm_closed_n", yesno(self.is_closed_n())), ro(t!("N closed").as_ref(), "pm_closed_n", yesno(self.is_closed_n())),
ro("M density", "pm_smooth_m", self.m_smooth_density.to_string()), ro(t!("M density").as_ref(), "pm_smooth_m", self.m_smooth_density.to_string()),
ro("N density", "pm_smooth_n", self.n_smooth_density.to_string()), ro(t!("N density").as_ref(), "pm_smooth_n", self.n_smooth_density.to_string()),
ro("Vertex count", "pm_v", self.vertices.len().to_string()), ro(t!("Vertex count").as_ref(), "pm_v", self.vertices.len().to_string()),
ro("Face count", "pm_faces", face_count.to_string()), ro(t!("Face count").as_ref(), "pm_faces", face_count.to_string()),
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ro("Fit/smooth", "pm_smooth", smooth)], props: vec![ro(t!("Fit/smooth").as_ref(), "pm_smooth", smooth)],
}, },
] ]
} }
@ -495,39 +493,36 @@ impl PropertyEditable for PolyfaceMesh {
let first = self.vertices.first(); let first = self.vertices.first();
vec![ vec![
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
ro("Vertex", "pfm_vertex", String::new()), ro(t!("Vertex").as_ref(), "pfm_vertex", String::new()),
ro( ro(t!("Vertex X").as_ref(),
"Vertex X",
"pfm_vx", "pfm_vx",
first.map(|v| format!("{:.4}", v.location.x)).unwrap_or_default(), first.map(|v| format!("{:.4}", v.location.x)).unwrap_or_default(),
), ),
ro( ro(t!("Vertex Y").as_ref(),
"Vertex Y",
"pfm_vy", "pfm_vy",
first.map(|v| format!("{:.4}", v.location.y)).unwrap_or_default(), first.map(|v| format!("{:.4}", v.location.y)).unwrap_or_default(),
), ),
ro( ro(t!("Vertex Z").as_ref(),
"Vertex Z",
"pfm_vz", "pfm_vz",
first.map(|v| format!("{:.4}", v.location.z)).unwrap_or_default(), first.map(|v| format!("{:.4}", v.location.z)).unwrap_or_default(),
), ),
// Polyface meshes store an explicit vertex/face list rather // Polyface meshes store an explicit vertex/face list rather
// than an M×N grid, so the grid-only rows are not applicable. // than an M×N grid, so the grid-only rows are not applicable.
ro("M vertex count", "pfm_m", String::new()), ro(t!("M vertex count").as_ref(), "pfm_m", String::new()),
ro("N vertex count", "pfm_n", String::new()), ro(t!("N vertex count").as_ref(), "pfm_n", String::new()),
ro("M closed", "pfm_closed_m", String::new()), ro(t!("M closed").as_ref(), "pfm_closed_m", String::new()),
ro("N closed", "pfm_closed_n", String::new()), ro(t!("N closed").as_ref(), "pfm_closed_n", String::new()),
ro("M density", "pfm_density_m", String::new()), ro(t!("M density").as_ref(), "pfm_density_m", String::new()),
ro("N density", "pfm_density_n", String::new()), ro(t!("N density").as_ref(), "pfm_density_n", String::new()),
ro("Vertex count", "pfm_v", self.vertices.len().to_string()), ro(t!("Vertex count").as_ref(), "pfm_v", self.vertices.len().to_string()),
ro("Face count", "pfm_f", self.faces.len().to_string()), ro(t!("Face count").as_ref(), "pfm_f", self.faces.len().to_string()),
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ro("Fit/smooth", "pfm_smooth", smooth)], props: vec![ro(t!("Fit/smooth").as_ref(), "pfm_smooth", smooth)],
}, },
] ]
} }
@ -1149,26 +1144,25 @@ impl PropertyEditable for Mesh {
let watertight = let watertight =
!self.faces.is_empty() && edge_use.values().all(|&c| c == 2); !self.faces.is_empty() && edge_use.values().all(|&c| c == 2);
vec![PropSection { vec![PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
Property { Property {
label: "Level of Smoothness".into(), label: t!("Level of Smoothness").into_owned(),
field: "msh_subdiv_edit", field: "msh_subdiv_edit",
value: PropValue::EditText(self.subdivision_level.to_string()), value: PropValue::EditText(self.subdivision_level.to_string()),
}, },
Property { Property {
label: "Blend Creases".into(), label: t!("Blend Creases").into_owned(),
field: "msh_blend_crease", field: "msh_blend_crease",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "msh_blend_crease", field: "msh_blend_crease",
value: self.blend_crease, value: self.blend_crease,
}, },
}, },
ro("Number of Faces", "msh_f", self.faces.len().to_string()), ro(t!("Number of Faces").as_ref(), "msh_f", self.faces.len().to_string()),
ro("Number of Vertices", "msh_v", self.vertices.len().to_string()), ro(t!("Number of Vertices").as_ref(), "msh_v", self.vertices.len().to_string()),
ro("Number of Edges", "msh_e", self.edges.len().to_string()), ro(t!("Number of Edges").as_ref(), "msh_e", self.edges.len().to_string()),
ro( ro(t!("Creased Edges").as_ref(),
"Creased Edges",
"msh_creased", "msh_creased",
self.edges self.edges
.iter() .iter()
@ -1176,14 +1170,12 @@ impl PropertyEditable for Mesh {
.count() .count()
.to_string(), .to_string(),
), ),
ro( ro(t!("Override Option").as_ref(),
"Override Option",
"msh_override", "msh_override",
self.override_option.to_string(), self.override_option.to_string(),
), ),
ro("Number of Grips", "msh_grips", self.vertices.len().to_string()), ro(t!("Number of Grips").as_ref(), "msh_grips", self.vertices.len().to_string()),
ro( ro(t!("Watertight").as_ref(),
"Watertight",
"msh_watertight", "msh_watertight",
if watertight { "Yes" } else { "No" }, if watertight { "Yes" } else { "No" },
), ),

View file

@ -1,4 +1,5 @@
use acadrust::entities::MLine; use acadrust::entities::MLine;
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{edit_prop as edit, ro_prop as ro, square_grip}; use crate::entities::common::{edit_prop as edit, ro_prop as ro, square_grip};
@ -336,24 +337,24 @@ impl PropertyEditable for MLine {
let cur_z = cur.map(|v| v.position.z).unwrap_or(0.0); let cur_z = cur.map(|v| v.position.z).unwrap_or(0.0);
vec![ vec![
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
ro("Vertex", "ml_vertex", if self.vertices.is_empty() { String::new() } else { "1".to_string() }), ro(t!("Vertex").as_ref(), "ml_vertex", if self.vertices.is_empty() { String::new() } else { "1".to_string() }),
edit("Vertex X", "ml_vertex_x", cur_x), edit(t!("Vertex X").as_ref(), "ml_vertex_x", cur_x),
edit("Vertex Y", "ml_vertex_y", cur_y), edit(t!("Vertex Y").as_ref(), "ml_vertex_y", cur_y),
edit("Vertex Z", "ml_vertex_z", cur_z), edit(t!("Vertex Z").as_ref(), "ml_vertex_z", cur_z),
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
Property { Property {
label: "Style".into(), label: t!("Style").into_owned(),
field: "ml_style", field: "ml_style",
value: PropValue::EditText(self.style_name.clone()), value: PropValue::EditText(self.style_name.clone()),
}, },
Property { Property {
label: "Style justification".into(), label: t!("Style justification").into_owned(),
field: "ml_justification", field: "ml_justification",
value: PropValue::Choice { value: PropValue::Choice {
selected: just_str.to_string(), selected: just_str.to_string(),
@ -363,7 +364,7 @@ impl PropertyEditable for MLine {
.collect(), .collect(),
}, },
}, },
edit("Style scale", "ml_scale", self.scale_factor), edit(t!("Style scale").as_ref(), "ml_scale", self.scale_factor),
], ],
}, },
] ]

View file

@ -11,6 +11,7 @@ use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckC
use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject}; use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Property}; use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Property};
use crate::scene::model::wire_model::SnapHint; use crate::scene::model::wire_model::SnapHint;
use crate::t;
/// The entity's `column_data` as the layout's column description. /// The entity's `column_data` as the layout's column description.
/// ///
@ -254,15 +255,15 @@ fn properties(t: &MText, text_style_names: &[String]) -> Vec<PropSection> {
let height_editable = col_type == 1 || (col_type == 2 && !t.column_data.auto_height); let height_editable = col_type == 1 || (col_type == 2 && !t.column_data.auto_height);
vec![ vec![
PropSection { PropSection {
title: "Text".into(), title: t!("Text").into_owned(),
props: vec![ props: vec![
Property { Property {
label: "Contents".into(), label: t!("Contents").into_owned(),
field: "content", field: "content",
value: PropValue::EditText(t.value.clone()), value: PropValue::EditText(t.value.clone()),
}, },
Property { Property {
label: "Style".into(), label: t!("Style").into_owned(),
field: "style", field: "style",
value: PropValue::Choice { value: PropValue::Choice {
selected: if t.style.trim().is_empty() { selected: if t.style.trim().is_empty() {
@ -274,12 +275,12 @@ fn properties(t: &MText, text_style_names: &[String]) -> Vec<PropSection> {
}, },
}, },
ro( ro(
"Annotative", t!("Annotative").as_ref(),
"annotative", "annotative",
if t.is_annotative { "Yes" } else { "No" }, if t.is_annotative { "Yes" } else { "No" },
), ),
Property { Property {
label: "Justify".into(), label: t!("Justify").into_owned(),
field: "justify", field: "justify",
value: PropValue::Choice { value: PropValue::Choice {
selected: attachment_str(&t.attachment_point).to_string(), selected: attachment_str(&t.attachment_point).to_string(),
@ -300,7 +301,7 @@ fn properties(t: &MText, text_style_names: &[String]) -> Vec<PropSection> {
}, },
}, },
Property { Property {
label: "Direction".into(), label: t!("Direction").into_owned(),
field: "direction", field: "direction",
value: PropValue::Choice { value: PropValue::Choice {
selected: drawing_dir_str(&t.drawing_direction).to_string(), selected: drawing_dir_str(&t.drawing_direction).to_string(),
@ -310,12 +311,12 @@ fn properties(t: &MText, text_style_names: &[String]) -> Vec<PropSection> {
.collect(), .collect(),
}, },
}, },
edit("Text height", "height", t.height), edit(t!("Text height").as_ref(), "height", t.height),
edit_angle("Rotation", "rotation", t.rotation.to_degrees()), edit_angle(t!("Rotation").as_ref(), "rotation", t.rotation.to_degrees()),
edit("Line space factor", "line_spacing", t.line_spacing_factor), edit(t!("Line space factor").as_ref(), "line_spacing", t.line_spacing_factor),
edit("Line space distance", "line_space_distance", line_space_distance), edit(t!("Line space distance").as_ref(), "line_space_distance", line_space_distance),
Property { Property {
label: "Line space style".into(), label: t!("Line space style").into_owned(),
field: "line_space_style", field: "line_space_style",
value: PropValue::Choice { value: PropValue::Choice {
selected: match t.line_spacing_style { selected: match t.line_spacing_style {
@ -330,7 +331,7 @@ fn properties(t: &MText, text_style_names: &[String]) -> Vec<PropSection> {
}, },
}, },
Property { Property {
label: "Background mask".into(), label: t!("Background mask").into_owned(),
field: "background_mask", field: "background_mask",
value: PropValue::Choice { value: PropValue::Choice {
selected: if t.background_fill_flags & 0x01 != 0 { selected: if t.background_fill_flags & 0x01 != 0 {
@ -346,15 +347,15 @@ fn properties(t: &MText, text_style_names: &[String]) -> Vec<PropSection> {
.collect(), .collect(),
}, },
}, },
num_row("Defined width", "rect_w", t.rectangle_width, width_editable), num_row(t!("Defined width").as_ref(), "rect_w", t.rectangle_width, width_editable),
num_row( num_row(
"Defined height", t!("Defined height").as_ref(),
"rect_h", "rect_h",
t.rectangle_height.unwrap_or(0.0), t.rectangle_height.unwrap_or(0.0),
height_editable, height_editable,
), ),
Property { Property {
label: "Columns".into(), label: t!("Columns").into_owned(),
field: "columns", field: "columns",
value: PropValue::Choice { value: PropValue::Choice {
selected: columns_str(&t.column_data).to_string(), selected: columns_str(&t.column_data).to_string(),
@ -366,20 +367,20 @@ fn properties(t: &MText, text_style_names: &[String]) -> Vec<PropSection> {
}, },
// Count / width / gutter are live only when columns are on. // Count / width / gutter are live only when columns are on.
num_row( num_row(
"Column count", t!("Column count").as_ref(),
"col_count", "col_count",
t.column_data.column_count as f64, t.column_data.column_count as f64,
col_type != 0, col_type != 0,
), ),
num_row("Column width", "col_width", t.column_data.width, col_type != 0), num_row(t!("Column width").as_ref(), "col_width", t.column_data.width, col_type != 0),
num_row( num_row(
"Column gutter", t!("Column gutter").as_ref(),
"col_gutter", "col_gutter",
t.column_data.gutter, t.column_data.gutter,
col_type != 0, col_type != 0,
), ),
Property { Property {
label: "Text frame".into(), label: t!("Text frame").into_owned(),
field: "text_frame", field: "text_frame",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "text_frame", field: "text_frame",
@ -389,11 +390,11 @@ fn properties(t: &MText, text_style_names: &[String]) -> Vec<PropSection> {
], ],
}, },
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Position X", "ins_x", t.insertion_point.x), edit(t!("Position X").as_ref(), "ins_x", t.insertion_point.x),
edit("Position Y", "ins_y", t.insertion_point.y), edit(t!("Position Y").as_ref(), "ins_y", t.insertion_point.y),
edit("Position Z", "ins_z", t.insertion_point.z), edit(t!("Position Z").as_ref(), "ins_z", t.insertion_point.z),
], ],
}, },
] ]

View file

@ -6,6 +6,7 @@ use acadrust::entities::{
use crate::entities::text_support::{ use crate::entities::text_support::{
layout_mtext, resolve_text_style, MTextRenderOpts, MTextVAnchor, ResolvedTextStyle, layout_mtext, resolve_text_style, MTextRenderOpts, MTextVAnchor, ResolvedTextStyle,
}; };
use crate::t;
/// Map MLEADER's vertical attachment enum onto the shared `MTextVAnchor` /// Map MLEADER's vertical attachment enum onto the shared `MTextVAnchor`
/// used by `layout_mtext`. Replaces the old `v_offset_for_attachment` /// used by `layout_mtext`. Replaces the old `v_offset_for_attachment`
@ -650,19 +651,19 @@ fn properties(ml: &MultiLeader) -> Vec<PropSection> {
// ── Misc ───────────────────────────────────────────────────────────── // ── Misc ─────────────────────────────────────────────────────────────
let misc = PropSection { let misc = PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
// Overall scale is grayed when annotative (annotation scale drives sizing). // Overall scale is grayed when annotative (annotation scale drives sizing).
num_row( num_row(
"Overall scale", t!("Overall scale").as_ref(),
"scale_factor", "scale_factor",
ml.scale_factor, ml.scale_factor,
!ml.enable_annotation_scale, !ml.enable_annotation_scale,
), ),
// Style name is resolved from style_handle by the panel builder (needs doc). // Style name is resolved from style_handle by the panel builder (needs doc).
ro("Multileader style", "mleader_style", "Standard"), ro(t!("Multileader style").as_ref(), "mleader_style", "Standard"),
bool_toggle( bool_toggle(
"Annotative", t!("Annotative").as_ref(),
"enable_annotation_scale", "enable_annotation_scale",
ml.enable_annotation_scale, ml.enable_annotation_scale,
), ),
@ -673,127 +674,127 @@ fn properties(ml: &MultiLeader) -> Vec<PropSection> {
// Landing rows are folded in here: the standalone "Leader Structure" group // Landing rows are folded in here: the standalone "Leader Structure" group
// is a style-dialog tab, not a palette group. // is a style-dialog tab, not a palette group.
let leaders = PropSection { let leaders = PropSection {
title: "Leaders".into(), title: t!("Leaders").into_owned(),
props: vec![ props: vec![
choice( choice(
"Leader type", t!("Leader type").as_ref(),
"path_type", "path_type",
leader_type_str(&ml.path_type), leader_type_str(&ml.path_type),
&["Straight", "Spline", "None"], &["Straight", "Spline", "None"],
), ),
Property { Property {
label: "Leader color".into(), label: t!("Leader color").into_owned(),
field: "line_color", field: "line_color",
value: PropValue::ColorChoice(ml.line_color), value: PropValue::ColorChoice(ml.line_color),
}, },
// Linetype name resolved from line_type_handle by the panel builder. // Linetype name resolved from line_type_handle by the panel builder.
ro("Leader linetype", "line_type_handle", "ByBlock"), ro(t!("Leader linetype").as_ref(), "line_type_handle", "ByBlock"),
Property { Property {
label: "Leader lineweight".into(), label: t!("Leader lineweight").into_owned(),
field: "line_weight", field: "line_weight",
value: PropValue::LwChoice(ml.line_weight), value: PropValue::LwChoice(ml.line_weight),
}, },
// Arrowhead block name resolved by the panel builder (default "Closed filled"). // Arrowhead block name resolved by the panel builder (default "Closed filled").
ro("Arrowhead", "arrowhead_handle", "Closed filled"), ro(t!("Arrowhead").as_ref(), "arrowhead_handle", "Closed filled"),
edit("Arrowhead size", "arrowhead_size", ml.arrowhead_size), edit(t!("Arrowhead size").as_ref(), "arrowhead_size", ml.arrowhead_size),
bool_toggle("Horizontal Landing", "enable_dogleg", ml.enable_dogleg), bool_toggle(t!("Horizontal Landing").as_ref(), "enable_dogleg", ml.enable_dogleg),
num_row( num_row(
"Landing distance", t!("Landing distance").as_ref(),
"landing_distance", "landing_distance",
ml.dogleg_length, ml.dogleg_length,
ml.enable_dogleg, ml.enable_dogleg,
), ),
bool_toggle("Leader extension", "enable_landing", ml.enable_landing), bool_toggle(t!("Leader extension").as_ref(), "enable_landing", ml.enable_landing),
], ],
}; };
// ── Text (shown only for MText content) ────────────────────────────── // ── Text (shown only for MText content) ──────────────────────────────
let text = PropSection { let text = PropSection {
title: "Text".into(), title: t!("Text").into_owned(),
props: vec![ props: vec![
Property { Property {
label: "Contents".into(), label: t!("Contents").into_owned(),
field: "text_string", field: "text_string",
value: PropValue::EditText(ctx.text_string.clone()), value: PropValue::EditText(ctx.text_string.clone()),
}, },
// Text-style name resolved from text_style_handle by the panel builder. // Text-style name resolved from text_style_handle by the panel builder.
ro("Text style", "text_style_handle", "Standard"), ro(t!("Text style").as_ref(), "text_style_handle", "Standard"),
choice( choice(
"Justify", t!("Justify").as_ref(),
"text_alignment", "text_alignment",
text_align_str(&ml.text_alignment), text_align_str(&ml.text_alignment),
&["Left", "Center", "Right"], &["Left", "Center", "Right"],
), ),
choice( choice(
"Direction", t!("Direction").as_ref(),
"text_flow_direction", "text_flow_direction",
flow_dir_str(&ctx.text_flow_direction), flow_dir_str(&ctx.text_flow_direction),
&["By style", "Left to right", "Top to bottom"], &["By style", "Left to right", "Top to bottom"],
), ),
edit("Width", "text_width", ctx.text_width), edit(t!("Width").as_ref(), "text_width", ctx.text_width),
edit("Height", "text_height", ml.text_height), edit(t!("Height").as_ref(), "text_height", ml.text_height),
edit_angle("Rotation", "text_rotation", ctx.text_rotation.to_degrees()), edit_angle(t!("Rotation").as_ref(), "text_rotation", ctx.text_rotation.to_degrees()),
edit("Line space factor", "line_spacing", ctx.line_spacing_factor), edit(t!("Line space factor").as_ref(), "line_spacing", ctx.line_spacing_factor),
edit( edit(
"Line space distance", t!("Line space distance").as_ref(),
"line_space_distance", "line_space_distance",
ml.text_height * 1.666_666_666_666_667 * ctx.line_spacing_factor, ml.text_height * 1.666_666_666_666_667 * ctx.line_spacing_factor,
), ),
choice( choice(
"Line space style", t!("Line space style").as_ref(),
"line_space_style", "line_space_style",
line_style_str(&ctx.line_spacing_style), line_style_str(&ctx.line_spacing_style),
&["At least", "Exactly"], &["At least", "Exactly"],
), ),
bool_toggle( bool_toggle(
"Background mask", t!("Background mask").as_ref(),
"background_fill_enabled", "background_fill_enabled",
ctx.background_fill_enabled, ctx.background_fill_enabled,
), ),
choice( choice(
"Attachment type", t!("Attachment type").as_ref(),
"text_attachment_direction", "text_attachment_direction",
attach_dir_str(&ml.text_attachment_direction), attach_dir_str(&ml.text_attachment_direction),
&["Horizontal", "Vertical"], &["Horizontal", "Vertical"],
), ),
choice( choice(
"Left Attachment", t!("Left Attachment").as_ref(),
"text_left_attachment", "text_left_attachment",
attachment_str(&ml.text_left_attachment), attachment_str(&ml.text_left_attachment),
&ATTACH_LABELS, &ATTACH_LABELS,
), ),
choice( choice(
"Right Attachment", t!("Right Attachment").as_ref(),
"text_right_attachment", "text_right_attachment",
attachment_str(&ml.text_right_attachment), attachment_str(&ml.text_right_attachment),
&ATTACH_LABELS, &ATTACH_LABELS,
), ),
edit("Landing gap", "landing_gap", ctx.landing_gap), edit(t!("Landing gap").as_ref(), "landing_gap", ctx.landing_gap),
bool_toggle("Text frame", "text_frame", ml.text_frame), bool_toggle(t!("Text frame").as_ref(), "text_frame", ml.text_frame),
], ],
}; };
// ── Block ──────────────────────────────────────────────────────────── // ── Block ────────────────────────────────────────────────────────────
let block = PropSection { let block = PropSection {
title: "Block".into(), title: t!("Block").into_owned(),
props: vec![ props: vec![
ro( ro(
"Source block", t!("Source block").as_ref(),
"block_content_handle", "block_content_handle",
hexh(ml.block_content_handle), hexh(ml.block_content_handle),
), ),
ro( ro(
"Block connection", t!("Block connection").as_ref(),
"block_connection_type", "block_connection_type",
format!("{:?}", ml.block_connection_type), format!("{:?}", ml.block_connection_type),
), ),
ro( ro(
"Block color", t!("Block color").as_ref(),
"block_content_color", "block_content_color",
format!("{:?}", ml.block_content_color), format!("{:?}", ml.block_content_color),
), ),
ro( ro(
"Block scale", t!("Block scale").as_ref(),
"block_scale", "block_scale",
format!( format!(
"{:.3} × {:.3} × {:.3}", "{:.3} × {:.3} × {:.3}",
@ -801,7 +802,7 @@ fn properties(ml: &MultiLeader) -> Vec<PropSection> {
), ),
), ),
edit( edit(
"Block rotation", t!("Block rotation").as_ref(),
"block_rotation", "block_rotation",
ml.block_rotation.to_degrees(), ml.block_rotation.to_degrees(),
), ),

View file

@ -1,5 +1,6 @@
use acadrust::entities::Point; use acadrust::entities::Point;
use acadrust::EntityType; use acadrust::EntityType;
use crate::t;
use truck_modeling::{builder, Point3}; use truck_modeling::{builder, Point3};
use crate::command::EntityTransform; use crate::command::EntityTransform;
@ -187,11 +188,11 @@ fn grips(pt: &Point) -> Vec<GripDef> {
fn properties(pt: &Point) -> Vec<PropSection> { fn properties(pt: &Point) -> Vec<PropSection> {
vec![PropSection { vec![PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Position X", "loc_x", pt.location.x), edit(t!("Position X").as_ref(), "loc_x", pt.location.x),
edit("Position Y", "loc_y", pt.location.y), edit(t!("Position Y").as_ref(), "loc_y", pt.location.y),
edit("Position Z", "loc_z", pt.location.z), edit(t!("Position Z").as_ref(), "loc_z", pt.location.z),
], ],
}] }]
} }

View file

@ -1,4 +1,5 @@
use acadrust::entities::{Polyline, Polyline2D, Polyline3D}; use acadrust::entities::{Polyline, Polyline2D, Polyline3D};
use crate::t;
use truck_modeling::{builder, Edge, Point3, Wire}; use truck_modeling::{builder, Edge, Point3, Wire};
use crate::command::EntityTransform; use crate::command::EntityTransform;
@ -123,11 +124,11 @@ impl Grippable for Polyline {
impl PropertyEditable for Polyline { impl PropertyEditable for Polyline {
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> { fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
vec![PropSection { vec![PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
ro("Vertices", "vertices", self.vertices.len().to_string()), ro(t!("Vertices").as_ref(), "vertices", self.vertices.len().to_string()),
Property { Property {
label: "Closed".into(), label: t!("Closed").into_owned(),
field: "pl_closed", field: "pl_closed",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "pl_closed", field: "pl_closed",
@ -565,24 +566,24 @@ impl PropertyEditable for Polyline2D {
vec![ vec![
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
stepper("Current Vertex", "pl2_current_vertex", vertex_label), stepper(t!("Current Vertex").as_ref(), "pl2_current_vertex", vertex_label),
edit("Vertex X", "pl2_vertex_x", vertex_x), edit(t!("Vertex X").as_ref(), "pl2_vertex_x", vertex_x),
edit("Vertex Y", "pl2_vertex_y", vertex_y), edit(t!("Vertex Y").as_ref(), "pl2_vertex_y", vertex_y),
edit("Start segment width", "pl2_seg_start_w", seg_start_w), edit(t!("Start segment width").as_ref(), "pl2_seg_start_w", seg_start_w),
edit("End segment width", "pl2_seg_end_w", seg_end_w), edit(t!("End segment width").as_ref(), "pl2_seg_end_w", seg_end_w),
edit("Global width", "pl2_start_w", self.start_width), edit(t!("Global width").as_ref(), "pl2_start_w", self.start_width),
edit("Elevation", "pl2_elevation", self.elevation), edit(t!("Elevation").as_ref(), "pl2_elevation", self.elevation),
ro("Area", "pl2_area", format!("{area:.4}")), ro(t!("Area").as_ref(), "pl2_area", format!("{area:.4}")),
ro("Length", "pl2_length", format!("{length:.4}")), ro(t!("Length").as_ref(), "pl2_length", format!("{length:.4}")),
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
Property { Property {
label: "Closed".into(), label: t!("Closed").into_owned(),
field: "pl2_closed", field: "pl2_closed",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "pl2_closed", field: "pl2_closed",
@ -590,7 +591,7 @@ impl PropertyEditable for Polyline2D {
}, },
}, },
Property { Property {
label: "Linetype generation".into(), label: t!("Linetype generation").into_owned(),
field: "pl2_ltype_gen", field: "pl2_ltype_gen",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "pl2_ltype_gen", field: "pl2_ltype_gen",
@ -838,26 +839,26 @@ impl PropertyEditable for Polyline3D {
vec![ vec![
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
ro("Vertex", "pl3_vertex", if n > 0 { "1" } else { "" }), ro(t!("Vertex").as_ref(), "pl3_vertex", if n > 0 { "1" } else { "" }),
edit("Vertex X", "pl3_vertex_x", vertex_x), edit(t!("Vertex X").as_ref(), "pl3_vertex_x", vertex_x),
edit("Vertex Y", "pl3_vertex_y", vertex_y), edit(t!("Vertex Y").as_ref(), "pl3_vertex_y", vertex_y),
edit("Vertex Z", "pl3_vertex_z", vertex_z), edit(t!("Vertex Z").as_ref(), "pl3_vertex_z", vertex_z),
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
Property { Property {
label: "Closed".into(), label: t!("Closed").into_owned(),
field: "pl3_closed", field: "pl3_closed",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "pl3_closed", field: "pl3_closed",
value: self.is_closed(), value: self.is_closed(),
}, },
}, },
ro("Fit/Smooth", "pl3_smooth", fit_smooth), ro(t!("Fit/Smooth").as_ref(), "pl3_smooth", fit_smooth),
], ],
}, },
] ]

View file

@ -1,4 +1,5 @@
use acadrust::entities::{RasterImage, Wipeout}; use acadrust::entities::{RasterImage, Wipeout};
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{center_grip, edit_prop as edit, ro_prop as ro, square_grip}; use crate::entities::common::{center_grip, edit_prop as edit, ro_prop as ro, square_grip};
@ -289,27 +290,27 @@ impl PropertyEditable for RasterImage {
let transparency = format!("{:.0}%", self.common.transparency.as_percent() * 100.0); let transparency = format!("{:.0}%", self.common.transparency.as_percent() * 100.0);
vec![ vec![
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Position X", "ri_ox", self.insertion_point.x), edit(t!("Position X").as_ref(), "ri_ox", self.insertion_point.x),
edit("Position Y", "ri_oy", self.insertion_point.y), edit(t!("Position Y").as_ref(), "ri_oy", self.insertion_point.y),
edit("Position Z", "ri_oz", self.insertion_point.z), edit(t!("Position Z").as_ref(), "ri_oz", self.insertion_point.z),
ro("Rotation", "ri_rotation", format!("{:.4}", rotation_deg)), ro(t!("Rotation").as_ref(), "ri_rotation", format!("{:.4}", rotation_deg)),
ro("Width", "ri_width", format!("{:.4}", self.width())), ro(t!("Width").as_ref(), "ri_width", format!("{:.4}", self.width())),
ro("Height", "ri_height", format!("{:.4}", self.height())), ro(t!("Height").as_ref(), "ri_height", format!("{:.4}", self.height())),
ro("Scale", "ri_scale", format!("{:.4}", scale)), ro(t!("Scale").as_ref(), "ri_scale", format!("{:.4}", scale)),
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
ro("Name", "ri_name", self.file_name().to_string()), ro(t!("Name").as_ref(), "ri_name", self.file_name().to_string()),
edit("Brightness", "ri_bright", self.brightness as f64), edit(t!("Brightness").as_ref(), "ri_bright", self.brightness as f64),
edit("Contrast", "ri_contrast", self.contrast as f64), edit(t!("Contrast").as_ref(), "ri_contrast", self.contrast as f64),
edit("Fade", "ri_fade", self.fade as f64), edit(t!("Fade").as_ref(), "ri_fade", self.fade as f64),
ro("Transparency", "ri_transparency", transparency), ro(t!("Transparency").as_ref(), "ri_transparency", transparency),
Property { Property {
label: "Show image".into(), label: t!("Show image").into_owned(),
field: "ri_show_image", field: "ri_show_image",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "ri_show_image", field: "ri_show_image",
@ -317,7 +318,7 @@ impl PropertyEditable for RasterImage {
}, },
}, },
Property { Property {
label: "Show clipped".into(), label: t!("Show clipped").into_owned(),
field: "ri_show_clipped", field: "ri_show_clipped",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "ri_show_clipped", field: "ri_show_clipped",
@ -325,7 +326,7 @@ impl PropertyEditable for RasterImage {
}, },
}, },
Property { Property {
label: "Clip inverted".into(), label: t!("Clip inverted").into_owned(),
field: "ri_clip_inverted", field: "ri_clip_inverted",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "ri_clip_inverted", field: "ri_clip_inverted",
@ -596,18 +597,18 @@ impl PropertyEditable for Wipeout {
.contains(acadrust::entities::WipeoutDisplayFlags::TRANSPARENCY_ON); .contains(acadrust::entities::WipeoutDisplayFlags::TRANSPARENCY_ON);
vec![ vec![
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Position X", "wo_ox", self.insertion_point.x), edit(t!("Position X").as_ref(), "wo_ox", self.insertion_point.x),
edit("Position Y", "wo_oy", self.insertion_point.y), edit(t!("Position Y").as_ref(), "wo_oy", self.insertion_point.y),
edit("Position Z", "wo_oz", self.insertion_point.z), edit(t!("Position Z").as_ref(), "wo_oz", self.insertion_point.z),
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
Property { Property {
label: "Show image".into(), label: t!("Show image").into_owned(),
field: "wo_show_image", field: "wo_show_image",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "wo_show_image", field: "wo_show_image",
@ -615,7 +616,7 @@ impl PropertyEditable for Wipeout {
}, },
}, },
Property { Property {
label: "Show clipped".into(), label: t!("Show clipped").into_owned(),
field: "wo_show_clipped", field: "wo_show_clipped",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "wo_show_clipped", field: "wo_show_clipped",
@ -623,7 +624,7 @@ impl PropertyEditable for Wipeout {
}, },
}, },
Property { Property {
label: "Background transparency".into(), label: t!("Background transparency").into_owned(),
field: "wo_bg_transparency", field: "wo_bg_transparency",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "wo_bg_transparency", field: "wo_bg_transparency",

View file

@ -1,4 +1,5 @@
use acadrust::entities::{Ray, XLine}; use acadrust::entities::{Ray, XLine};
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{center_grip, edit_prop as edit, square_grip}; use crate::entities::common::{center_grip, edit_prop as edit, square_grip};
@ -91,14 +92,14 @@ impl Grippable for Ray {
impl PropertyEditable for Ray { impl PropertyEditable for Ray {
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> { fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
vec![PropSection { vec![PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Start X", "ray_bx", self.base_point.x), edit(t!("Start X").as_ref(), "ray_bx", self.base_point.x),
edit("Start Y", "ray_by", self.base_point.y), edit(t!("Start Y").as_ref(), "ray_by", self.base_point.y),
edit("Start Z", "ray_bz", self.base_point.z), edit(t!("Start Z").as_ref(), "ray_bz", self.base_point.z),
edit("Direction vector X", "ray_dx", self.direction.x), edit(t!("Direction vector X").as_ref(), "ray_dx", self.direction.x),
edit("Direction vector Y", "ray_dy", self.direction.y), edit(t!("Direction vector Y").as_ref(), "ray_dy", self.direction.y),
edit("Direction vector Z", "ray_dz", self.direction.z), edit(t!("Direction vector Z").as_ref(), "ray_dz", self.direction.z),
], ],
}] }]
} }
@ -229,14 +230,14 @@ impl Grippable for XLine {
impl PropertyEditable for XLine { impl PropertyEditable for XLine {
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> { fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
vec![PropSection { vec![PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Base X", "xl_bx", self.base_point.x), edit(t!("Base X").as_ref(), "xl_bx", self.base_point.x),
edit("Base Y", "xl_by", self.base_point.y), edit(t!("Base Y").as_ref(), "xl_by", self.base_point.y),
edit("Base Z", "xl_bz", self.base_point.z), edit(t!("Base Z").as_ref(), "xl_bz", self.base_point.z),
edit("Direction vector X", "xl_dx", self.direction.x), edit(t!("Direction vector X").as_ref(), "xl_dx", self.direction.x),
edit("Direction vector Y", "xl_dy", self.direction.y), edit(t!("Direction vector Y").as_ref(), "xl_dy", self.direction.y),
edit("Direction vector Z", "xl_dz", self.direction.z), edit(t!("Direction vector Z").as_ref(), "xl_dz", self.direction.z),
], ],
}] }]
} }

View file

@ -6,6 +6,7 @@
// gives a rough indication of the glyph orientation. // gives a rough indication of the glyph orientation.
use acadrust::entities::Shape; use acadrust::entities::Shape;
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{edit_angle_prop as edit_angle, edit_prop as edit, ro_prop as ro, square_grip}; use crate::entities::common::{edit_angle_prop as edit_angle, edit_prop as edit, ro_prop as ro, square_grip};
@ -254,29 +255,29 @@ impl PropertyEditable for Shape {
_ => "(none)".to_string(), _ => "(none)".to_string(),
}; };
vec![PropSection { vec![PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
ro("Name", "shp_name", self.shape_name.clone()), ro(t!("Name").as_ref(), "shp_name", self.shape_name.clone()),
ro("Number", "shp_number", self.shape_number.to_string()), ro(t!("Number").as_ref(), "shp_number", self.shape_number.to_string()),
Property { Property {
label: "Style".into(), label: t!("Style").into_owned(),
field: "shp_style", field: "shp_style",
value: PropValue::Choice { value: PropValue::Choice {
selected: self.style_name.clone(), selected: self.style_name.clone(),
options: text_style_names.to_vec(), options: text_style_names.to_vec(),
}, },
}, },
ro("Style Handle", "shp_style_handle", style_handle_display), ro(t!("Style Handle").as_ref(), "shp_style_handle", style_handle_display),
edit("Insert X", "shp_ix", self.insertion_point.x), edit(t!("Insert X").as_ref(), "shp_ix", self.insertion_point.x),
edit("Insert Y", "shp_iy", self.insertion_point.y), edit(t!("Insert Y").as_ref(), "shp_iy", self.insertion_point.y),
edit("Insert Z", "shp_iz", self.insertion_point.z), edit(t!("Insert Z").as_ref(), "shp_iz", self.insertion_point.z),
edit("Size", "shp_sz", self.size), edit(t!("Size").as_ref(), "shp_sz", self.size),
edit_angle("Rotation", "shp_rot", self.rotation.to_degrees()), edit_angle(t!("Rotation").as_ref(), "shp_rot", self.rotation.to_degrees()),
edit("Width Factor", "shp_xs", self.relative_x_scale), edit(t!("Width Factor").as_ref(), "shp_xs", self.relative_x_scale),
edit_angle("Oblique Angle", "shp_ob", self.oblique_angle.to_degrees()), edit_angle(t!("Oblique Angle").as_ref(), "shp_ob", self.oblique_angle.to_degrees()),
edit("Normal X", "shp_nx", self.normal.x), edit(t!("Normal X").as_ref(), "shp_nx", self.normal.x),
edit("Normal Y", "shp_ny", self.normal.y), edit(t!("Normal Y").as_ref(), "shp_ny", self.normal.y),
edit("Normal Z", "shp_nz", self.normal.z), edit(t!("Normal Z").as_ref(), "shp_nz", self.normal.z),
], ],
}] }]
} }

View file

@ -8,6 +8,7 @@
// Grips: 4 corner grip points. // Grips: 4 corner grip points.
use acadrust::entities::Solid; use acadrust::entities::Solid;
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{edit_prop as edit, ro_prop as ro, square_grip}; use crate::entities::common::{edit_prop as edit, ro_prop as ro, square_grip};
@ -153,21 +154,21 @@ impl PropertyEditable for Solid {
// field on the entity); reported from the first corner's Z. // field on the entity); reported from the first corner's Z.
let elevation = self.first_corner.z; let elevation = self.first_corner.z;
vec![PropSection { vec![PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Point 1 X", "sl_p1x", self.first_corner.x), edit(t!("Point 1 X").as_ref(), "sl_p1x", self.first_corner.x),
edit("Point 1 Y", "sl_p1y", self.first_corner.y), edit(t!("Point 1 Y").as_ref(), "sl_p1y", self.first_corner.y),
edit("Point 1 Z", "sl_p1z", self.first_corner.z), edit(t!("Point 1 Z").as_ref(), "sl_p1z", self.first_corner.z),
edit("Point 2 X", "sl_p2x", self.second_corner.x), edit(t!("Point 2 X").as_ref(), "sl_p2x", self.second_corner.x),
edit("Point 2 Y", "sl_p2y", self.second_corner.y), edit(t!("Point 2 Y").as_ref(), "sl_p2y", self.second_corner.y),
edit("Point 2 Z", "sl_p2z", self.second_corner.z), edit(t!("Point 2 Z").as_ref(), "sl_p2z", self.second_corner.z),
edit("Point 3 X", "sl_p3x", self.third_corner.x), edit(t!("Point 3 X").as_ref(), "sl_p3x", self.third_corner.x),
edit("Point 3 Y", "sl_p3y", self.third_corner.y), edit(t!("Point 3 Y").as_ref(), "sl_p3y", self.third_corner.y),
edit("Point 3 Z", "sl_p3z", self.third_corner.z), edit(t!("Point 3 Z").as_ref(), "sl_p3z", self.third_corner.z),
edit("Point 4 X", "sl_p4x", self.fourth_corner.x), edit(t!("Point 4 X").as_ref(), "sl_p4x", self.fourth_corner.x),
edit("Point 4 Y", "sl_p4y", self.fourth_corner.y), edit(t!("Point 4 Y").as_ref(), "sl_p4y", self.fourth_corner.y),
edit("Point 4 Z", "sl_p4z", self.fourth_corner.z), edit(t!("Point 4 Z").as_ref(), "sl_p4z", self.fourth_corner.z),
ro("Elevation", "sl_elev", format!("{:.4}", elevation)), ro(t!("Elevation").as_ref(), "sl_elev", format!("{:.4}", elevation)),
], ],
}] }]
} }

View file

@ -7,6 +7,7 @@
// the MeshModel vertices to match. // the MeshModel vertices to match.
use acadrust::entities::{Body, Region, Solid3D, Surface}; use acadrust::entities::{Body, Region, Solid3D, Surface};
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{center_grip, edit_prop as edit, parse_f64, ro_prop as ro}; use crate::entities::common::{center_grip, edit_prop as edit, parse_f64, ro_prop as ro};
use crate::entities::traits::{Grippable, PropertyEditable, Transformable}; use crate::entities::traits::{Grippable, PropertyEditable, Transformable};
@ -119,46 +120,40 @@ fn acis_sections(
); );
vec![ vec![
PropSection { PropSection {
title: "Modeler Geometry".into(), title: t!("Modeler Geometry").into_owned(),
props: vec![ props: vec![
ro( ro(t!("Format").as_ref(),
"Format",
"acis_format", "acis_format",
if acis.is_binary { "SAB" } else { "SAT" }, if acis.is_binary { "SAB" } else { "SAT" },
), ),
ro("Version", "acis_version", format!("{:?}", acis.version)), ro(t!("Version").as_ref(), "acis_version", format!("{:?}", acis.version)),
ro("Data Size", "acis_size", format!("{} bytes", acis.size())), ro(t!("Data Size").as_ref(), "acis_size", format!("{} bytes", acis.size())),
ro("Revision", "acis_revision", revision), ro(t!("Revision").as_ref(), "acis_revision", revision),
ro("Material Bindings", "acis_materials", bindings), ro(t!("Material Bindings").as_ref(), "acis_materials", bindings),
ro("Extra Modeler Data", "acis_extra", extra), ro(t!("Extra Modeler Data").as_ref(), "acis_extra", extra),
ro("History", "acis_history", handle_text(history)), ro(t!("History").as_ref(), "acis_history", handle_text(history)),
], ],
}, },
PropSection { PropSection {
title: "Modeler Display Data".into(), title: t!("Modeler Display Data").into_owned(),
props: vec![ props: vec![
ro( ro(t!("Wireframe Cache").as_ref(),
"Wireframe Cache",
"acis_wireframe", "acis_wireframe",
yes_no(acis.wireframe_data_present), yes_no(acis.wireframe_data_present),
), ),
ro( ro(t!("Reference Point").as_ref(),
"Reference Point",
"acis_wireframe_point", "acis_wireframe_point",
yes_no(acis.wireframe_point_present), yes_no(acis.wireframe_point_present),
), ),
ro( ro(t!("Isoline List").as_ref(),
"Isoline List",
"acis_wireframe_isolines", "acis_wireframe_isolines",
yes_no(acis.wireframe_isoline_present), yes_no(acis.wireframe_isoline_present),
), ),
ro( ro(t!("Isolines").as_ref(),
"Isolines",
"acis_isolines", "acis_isolines",
acis.wireframe_isolines.to_string(), acis.wireframe_isolines.to_string(),
), ),
ro( ro(t!("Wires").as_ref(),
"Wires",
"acis_wires", "acis_wires",
format!( format!(
"{} (unknown {}, silhouette {}, visible {}, hidden {}, isoline {}; {} points; {} transformed)", "{} (unknown {}, silhouette {}, visible {}, hidden {}, isoline {}; {} points; {} transformed)",
@ -172,8 +167,7 @@ fn acis_sections(
transformed transformed
), ),
), ),
ro( ro(t!("View Silhouettes").as_ref(),
"View Silhouettes",
"acis_silhouettes", "acis_silhouettes",
format!( format!(
"{} views; {} wires", "{} views; {} wires",
@ -194,11 +188,11 @@ fn position_section(prefix: &str, p: &acadrust::types::Vector3) -> PropSection {
_ => ["s3d_px", "s3d_py", "s3d_pz"], _ => ["s3d_px", "s3d_py", "s3d_pz"],
}; };
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Position X", fields[0], p.x), edit(t!("Position X").as_ref(), fields[0], p.x),
edit("Position Y", fields[1], p.y), edit(t!("Position Y").as_ref(), fields[1], p.y),
edit("Position Z", fields[2], p.z), edit(t!("Position Z").as_ref(), fields[2], p.z),
], ],
} }
} }
@ -259,7 +253,7 @@ impl PropertyEditable for Solid3D {
acis_sections(&self.acis_data, &self.wires, &self.silhouettes, self.history_handle); acis_sections(&self.acis_data, &self.wires, &self.silhouettes, self.history_handle);
sections[0] sections[0]
.props .props
.insert(0, ro("UID", "s3d_uid", self.uid.clone())); .insert(0, ro(t!("UID").as_ref(), "s3d_uid", self.uid.clone()));
sections.push(position_section("s3d", &self.point_of_reference)); sections.push(position_section("s3d", &self.point_of_reference));
sections sections
} }
@ -304,14 +298,14 @@ impl PropertyEditable for Region {
acis_sections(&self.acis_data, &self.wires, &self.silhouettes, self.history_handle); acis_sections(&self.acis_data, &self.wires, &self.silhouettes, self.history_handle);
sections[0] sections[0]
.props .props
.insert(0, ro("UID", "rgn_uid", self.uid.clone())); .insert(0, ro(t!("UID").as_ref(), "rgn_uid", self.uid.clone()));
let mut geometry = position_section("rgn", &self.point_of_reference); let mut geometry = position_section("rgn", &self.point_of_reference);
geometry geometry
.props .props
.push(ro("Area", "rgn_area", format!("{area:.4}"))); .push(ro(t!("Area").as_ref(), "rgn_area", format!("{area:.4}")));
geometry geometry
.props .props
.push(ro("Perimeter", "rgn_perimeter", format!("{perimeter:.4}"))); .push(ro(t!("Perimeter").as_ref(), "rgn_perimeter", format!("{perimeter:.4}")));
sections.push(geometry); sections.push(geometry);
sections sections
} }
@ -355,7 +349,7 @@ impl PropertyEditable for Body {
acis_sections(&self.acis_data, &self.wires, &self.silhouettes, self.history_handle); acis_sections(&self.acis_data, &self.wires, &self.silhouettes, self.history_handle);
sections[0] sections[0]
.props .props
.insert(0, ro("UID", "bdy_uid", self.uid.clone())); .insert(0, ro(t!("UID").as_ref(), "bdy_uid", self.uid.clone()));
sections.push(position_section("bdy", &self.point_of_reference)); sections.push(position_section("bdy", &self.point_of_reference));
sections sections
} }
@ -433,19 +427,16 @@ fn sweep_options_text(options: &acadrust::entities::SurfaceSweepOptions) -> Stri
fn surface_construction_section(surface: &Surface) -> PropSection { fn surface_construction_section(surface: &Surface) -> PropSection {
use acadrust::entities::SurfaceData; use acadrust::entities::SurfaceData;
let mut props = vec![ let mut props = vec![
ro("Kind", "srf_kind", format!("{:?}", surface.kind)), ro(t!("Kind").as_ref(), "srf_kind", format!("{:?}", surface.kind)),
ro( ro(t!("Modeler Format").as_ref(),
"Modeler Format",
"srf_modeler_version", "srf_modeler_version",
surface.modeler_format_version.to_string(), surface.modeler_format_version.to_string(),
), ),
edit( edit(t!("U Isolines").as_ref(),
"U Isolines",
"srf_u_isolines", "srf_u_isolines",
surface.u_isolines as f64, surface.u_isolines as f64,
), ),
edit( edit(t!("V Isolines").as_ref(),
"V Isolines",
"srf_v_isolines", "srf_v_isolines",
surface.v_isolines as f64, surface.v_isolines as f64,
), ),
@ -453,8 +444,7 @@ fn surface_construction_section(surface: &Surface) -> PropSection {
match &surface.surface_data { match &surface.surface_data {
SurfaceData::Generic => {} SurfaceData::Generic => {}
SurfaceData::Plane { class_version } => { SurfaceData::Plane { class_version } => {
props.push(ro( props.push(ro(t!("Class Version").as_ref(),
"Class Version",
"srf_class_version", "srf_class_version",
class_version.to_string(), class_version.to_string(),
)); ));
@ -466,25 +456,21 @@ fn surface_construction_section(surface: &Surface) -> PropSection {
sweep_transform, sweep_transform,
} => { } => {
props.extend([ props.extend([
ro( ro(t!("Sweep Entity").as_ref(),
"Sweep Entity",
"srf_sweep_entity", "srf_sweep_entity",
embedded_name(sweep_entity.as_ref()), embedded_name(sweep_entity.as_ref()),
), ),
ro("Sweep Vector", "srf_sweep_vector", vector_text(sweep_vector)), ro(t!("Sweep Vector").as_ref(), "srf_sweep_vector", vector_text(sweep_vector)),
ro("Sweep Options", "srf_sweep_options", sweep_options_text(options)), ro(t!("Sweep Options").as_ref(), "srf_sweep_options", sweep_options_text(options)),
ro( ro(t!("Sweep Transform").as_ref(),
"Sweep Transform",
"srf_sweep_transform", "srf_sweep_transform",
matrix_text(sweep_transform), matrix_text(sweep_transform),
), ),
ro( ro(t!("Sweep Entity Transform").as_ref(),
"Sweep Entity Transform",
"srf_sweep_entity_transform", "srf_sweep_entity_transform",
matrix_text(&options.sweep_entity_transform), matrix_text(&options.sweep_entity_transform),
), ),
ro( ro(t!("Path Entity Transform").as_ref(),
"Path Entity Transform",
"srf_path_entity_transform", "srf_path_entity_transform",
matrix_text(&options.path_entity_transform), matrix_text(&options.path_entity_transform),
), ),
@ -513,8 +499,7 @@ fn surface_construction_section(surface: &Surface) -> PropSection {
path_curve, path_curve,
} => { } => {
props.extend([ props.extend([
ro( ro(t!("Embedded Curves").as_ref(),
"Embedded Curves",
"srf_loft_embedded", "srf_loft_embedded",
format!( format!(
"{} cross sections; {} guides; path {}", "{} cross sections; {} guides; path {}",
@ -523,8 +508,7 @@ fn surface_construction_section(surface: &Surface) -> PropSection {
embedded_name(path_entity.as_ref()) embedded_name(path_entity.as_ref())
), ),
), ),
ro( ro(t!("Database Curves").as_ref(),
"Database Curves",
"srf_loft_handles", "srf_loft_handles",
format!( format!(
"{} cross sections; {} guides; path {}", "{} cross sections; {} guides; path {}",
@ -533,8 +517,7 @@ fn surface_construction_section(surface: &Surface) -> PropSection {
handle_text(*path_curve) handle_text(*path_curve)
), ),
), ),
ro( ro(t!("Draft").as_ref(),
"Draft",
"srf_loft_draft", "srf_loft_draft",
format!( format!(
"type {}; angle {:.6}→{:.6}; magnitude {:.6}→{:.6}", "type {}; angle {:.6}→{:.6}; magnitude {:.6}→{:.6}",
@ -545,8 +528,7 @@ fn surface_construction_section(surface: &Surface) -> PropSection {
end_draft_magnitude end_draft_magnitude
), ),
), ),
ro( ro(t!("Loft Flags").as_ref(),
"Loft Flags",
"srf_loft_flags", "srf_loft_flags",
format!( format!(
"arc-length {}; no-twist {}; align {}; simple {}; closed {}; solid {}; ruled {}; virtual-guide {}", "arc-length {}; no-twist {}; align {}; simple {}; closed {}; solid {}; ruled {}; virtual-guide {}",
@ -560,8 +542,7 @@ fn surface_construction_section(surface: &Surface) -> PropSection {
yes_no(*virtual_guide) yes_no(*virtual_guide)
), ),
), ),
ro( ro(t!("Loft Transform").as_ref(),
"Loft Transform",
"srf_loft_transform", "srf_loft_transform",
matrix_text(loft_transform), matrix_text(loft_transform),
), ),
@ -584,33 +565,28 @@ fn surface_construction_section(surface: &Surface) -> PropSection {
close_to_axis, close_to_axis,
} => { } => {
props.extend([ props.extend([
ro( ro(t!("Revolve Entity").as_ref(),
"Revolve Entity",
"srf_revolve_entity", "srf_revolve_entity",
embedded_name(revolve_entity.as_ref()), embedded_name(revolve_entity.as_ref()),
), ),
ro( ro(t!("Class / Entity").as_ref(),
"Class / Entity",
"srf_revolve_ids", "srf_revolve_ids",
format!("{class_version} / {entity_id}"), format!("{class_version} / {entity_id}"),
), ),
ro("Axis Point", "srf_axis_point", vector_text(axis_point)), ro(t!("Axis Point").as_ref(), "srf_axis_point", vector_text(axis_point)),
ro("Axis Vector", "srf_axis_vector", vector_text(axis_vector)), ro(t!("Axis Vector").as_ref(), "srf_axis_vector", vector_text(axis_vector)),
ro( ro(t!("Angles").as_ref(),
"Angles",
"srf_revolve_angles", "srf_revolve_angles",
format!( format!(
"start {:.6}; revolve {:.6}; draft {:.6}; twist {:.6}", "start {:.6}; revolve {:.6}; draft {:.6}; twist {:.6}",
start_angle, revolve_angle, draft_angle, twist_angle start_angle, revolve_angle, draft_angle, twist_angle
), ),
), ),
ro( ro(t!("Draft Distances").as_ref(),
"Draft Distances",
"srf_revolve_draft_distances", "srf_revolve_draft_distances",
format!("{draft_start_distance:.6}→{draft_end_distance:.6}"), format!("{draft_start_distance:.6}→{draft_end_distance:.6}"),
), ),
ro( ro(t!("Revolve Flags").as_ref(),
"Revolve Flags",
"srf_revolve_flags", "srf_revolve_flags",
format!( format!(
"solid {}; close-to-axis {}", "solid {}; close-to-axis {}",
@ -618,8 +594,7 @@ fn surface_construction_section(surface: &Surface) -> PropSection {
yes_no(*close_to_axis) yes_no(*close_to_axis)
), ),
), ),
ro( ro(t!("Entity Transform").as_ref(),
"Entity Transform",
"srf_revolve_transform", "srf_revolve_transform",
matrix_text(entity_transform), matrix_text(entity_transform),
), ),
@ -634,13 +609,11 @@ fn surface_construction_section(surface: &Surface) -> PropSection {
options, options,
} => { } => {
props.extend([ props.extend([
ro( ro(t!("Class Version").as_ref(),
"Class Version",
"srf_class_version", "srf_class_version",
class_version.to_string(), class_version.to_string(),
), ),
ro( ro(t!("Sweep / Path").as_ref(),
"Sweep / Path",
"srf_swept_entities", "srf_swept_entities",
format!( format!(
"{} / {}", "{} / {}",
@ -648,24 +621,20 @@ fn surface_construction_section(surface: &Surface) -> PropSection {
embedded_name(path_entity.as_ref()) embedded_name(path_entity.as_ref())
), ),
), ),
ro("Sweep Options", "srf_sweep_options", sweep_options_text(options)), ro(t!("Sweep Options").as_ref(), "srf_sweep_options", sweep_options_text(options)),
ro( ro(t!("Sweep Transform").as_ref(),
"Sweep Transform",
"srf_sweep_transform", "srf_sweep_transform",
matrix_text(sweep_transform), matrix_text(sweep_transform),
), ),
ro( ro(t!("Path Transform").as_ref(),
"Path Transform",
"srf_path_transform", "srf_path_transform",
matrix_text(path_transform), matrix_text(path_transform),
), ),
ro( ro(t!("Sweep Entity Transform").as_ref(),
"Sweep Entity Transform",
"srf_sweep_entity_transform", "srf_sweep_entity_transform",
matrix_text(&options.sweep_entity_transform), matrix_text(&options.sweep_entity_transform),
), ),
ro( ro(t!("Path Entity Transform").as_ref(),
"Path Entity Transform",
"srf_path_entity_transform", "srf_path_entity_transform",
matrix_text(&options.path_entity_transform), matrix_text(&options.path_entity_transform),
), ),
@ -680,25 +649,23 @@ fn surface_construction_section(surface: &Surface) -> PropSection {
v_vector2, v_vector2,
} => { } => {
props.extend([ props.extend([
ro( ro(t!("NURB Version").as_ref(),
"NURB Version",
"srf_nurb_version", "srf_nurb_version",
short_170.to_string(), short_170.to_string(),
), ),
ro( ro(t!("CV Hull").as_ref(),
"CV Hull",
"srf_nurb_cv_hull", "srf_nurb_cv_hull",
yes_no(*cv_hull_display), yes_no(*cv_hull_display),
), ),
ro("U Vector 1", "srf_nurb_u1", vector_text(u_vector1)), ro(t!("U Vector 1").as_ref(), "srf_nurb_u1", vector_text(u_vector1)),
ro("V Vector 1", "srf_nurb_v1", vector_text(v_vector1)), ro(t!("V Vector 1").as_ref(), "srf_nurb_v1", vector_text(v_vector1)),
ro("U Vector 2", "srf_nurb_u2", vector_text(u_vector2)), ro(t!("U Vector 2").as_ref(), "srf_nurb_u2", vector_text(u_vector2)),
ro("V Vector 2", "srf_nurb_v2", vector_text(v_vector2)), ro(t!("V Vector 2").as_ref(), "srf_nurb_v2", vector_text(v_vector2)),
]); ]);
} }
} }
PropSection { PropSection {
title: "Surface Construction".into(), title: t!("Surface Construction").into_owned(),
props, props,
} }
} }

View file

@ -1,4 +1,5 @@
use acadrust::entities::Spline; use acadrust::entities::Spline;
use crate::t;
use truck_modeling::{ use truck_modeling::{
base::{BoundedCurve, ParametricCurve, Vector4}, base::{BoundedCurve, ParametricCurve, Vector4},
builder, BSplineCurve, Curve, Edge, KnotVec, NurbsCurve, Point3, Wire, builder, BSplineCurve, Curve, Edge, KnotVec, NurbsCurve, Point3, Wire,
@ -383,58 +384,58 @@ fn properties(spline: &Spline) -> Vec<PropSection> {
vec![ vec![
PropSection { PropSection {
title: "Data Points".into(), title: t!("Data Points").into_owned(),
props: vec![ props: vec![
ro("Show", "show", show), ro(t!("Show").as_ref(), "show", show),
ro("Degree", "degree", spline.degree.to_string()), ro(t!("Degree").as_ref(), "degree", spline.degree.to_string()),
ro( ro(
"Control Point Count", t!("Control Point Count").as_ref(),
"ctrl_pt_count", "ctrl_pt_count",
spline.control_points.len().to_string(), spline.control_points.len().to_string(),
), ),
ro("Control Point", "ctrl_pt_index", "0"), ro(t!("Control Point").as_ref(), "ctrl_pt_index", "0"),
edit( edit(
"Control Point X", t!("Control Point X").as_ref(),
"ctrl_pt_x", "ctrl_pt_x",
cp0.map(|p| p.x).unwrap_or(0.0), cp0.map(|p| p.x).unwrap_or(0.0),
), ),
edit( edit(
"Control Point Y", t!("Control Point Y").as_ref(),
"ctrl_pt_y", "ctrl_pt_y",
cp0.map(|p| p.y).unwrap_or(0.0), cp0.map(|p| p.y).unwrap_or(0.0),
), ),
edit( edit(
"Control Point Z", t!("Control Point Z").as_ref(),
"ctrl_pt_z", "ctrl_pt_z",
cp0.map(|p| p.z).unwrap_or(0.0), cp0.map(|p| p.z).unwrap_or(0.0),
), ),
edit("Weight", "weight", w0.unwrap_or(1.0)), edit(t!("Weight").as_ref(), "weight", w0.unwrap_or(1.0)),
ro("Knot Parameterization", "knot_param", knot_param), ro(t!("Knot Parameterization").as_ref(), "knot_param", knot_param),
ro( ro(
"Fit Point Count", t!("Fit Point Count").as_ref(),
"fit_pt_count", "fit_pt_count",
spline.fit_points.len().to_string(), spline.fit_points.len().to_string(),
), ),
ro("Fit Point", "fit_pt_index", "0"), ro(t!("Fit Point").as_ref(), "fit_pt_index", "0"),
edit("Fit Point X", "fit_pt_x", fp0.map(|p| p.x).unwrap_or(0.0)), edit(t!("Fit Point X").as_ref(), "fit_pt_x", fp0.map(|p| p.x).unwrap_or(0.0)),
edit("Fit Point Y", "fit_pt_y", fp0.map(|p| p.y).unwrap_or(0.0)), edit(t!("Fit Point Y").as_ref(), "fit_pt_y", fp0.map(|p| p.y).unwrap_or(0.0)),
edit("Fit Point Z", "fit_pt_z", fp0.map(|p| p.z).unwrap_or(0.0)), edit(t!("Fit Point Z").as_ref(), "fit_pt_z", fp0.map(|p| p.z).unwrap_or(0.0)),
edit("Fit Tolerance", "fit_tolerance", spline.fit_tolerance), edit(t!("Fit Tolerance").as_ref(), "fit_tolerance", spline.fit_tolerance),
edit("Start Tangent X", "start_tan_x", spline.begin_tangent.x), edit(t!("Start Tangent X").as_ref(), "start_tan_x", spline.begin_tangent.x),
edit("Start Tangent Y", "start_tan_y", spline.begin_tangent.y), edit(t!("Start Tangent Y").as_ref(), "start_tan_y", spline.begin_tangent.y),
edit("Start Tangent Z", "start_tan_z", spline.begin_tangent.z), edit(t!("Start Tangent Z").as_ref(), "start_tan_z", spline.begin_tangent.z),
edit("End Tangent X", "end_tan_x", spline.end_tangent.x), edit(t!("End Tangent X").as_ref(), "end_tan_x", spline.end_tangent.x),
edit("End Tangent Y", "end_tan_y", spline.end_tangent.y), edit(t!("End Tangent Y").as_ref(), "end_tan_y", spline.end_tangent.y),
edit("End Tangent Z", "end_tan_z", spline.end_tangent.z), edit(t!("End Tangent Z").as_ref(), "end_tan_z", spline.end_tangent.z),
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
ro("Closed", "closed", yes_no(closed)), ro(t!("Closed").as_ref(), "closed", yes_no(closed)),
ro("Planar", "planar", yes_no(spline.flags.planar)), ro(t!("Planar").as_ref(), "planar", yes_no(spline.flags.planar)),
ro("Length", "length", format!("{length:.4}")), ro(t!("Length").as_ref(), "length", format!("{length:.4}")),
ro("Area", "area", area), ro(t!("Area").as_ref(), "area", area),
], ],
}, },
] ]

View file

@ -11,6 +11,7 @@ use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
use crate::scene::model::object::{GripApply, GripDef, PropSection, Property, PropValue}; use crate::scene::model::object::{GripApply, GripDef, PropSection, Property, PropValue};
use crate::scene::view::transform; use crate::scene::view::transform;
use crate::scene::model::wire_model::SnapHint; use crate::scene::model::wire_model::SnapHint;
use crate::t;
fn v3(v: &acadrust::types::Vector3) -> Vec3 { fn v3(v: &acadrust::types::Vector3) -> Vec3 {
Vec3::new(v.x as f32, v.y as f32, v.z as f32) Vec3::new(v.x as f32, v.y as f32, v.z as f32)
@ -1773,7 +1774,7 @@ impl PropertyEditable for Table {
_ => "(none)".to_string(), _ => "(none)".to_string(),
} }
}; };
let toggle = |label: &'static str, field: &'static str, b: bool| -> Property { let toggle = |label: &str, field: &'static str, b: bool| -> Property {
Property { Property {
label: label.into(), label: label.into(),
field, field,
@ -1806,93 +1807,93 @@ impl PropertyEditable for Table {
vec![ vec![
PropSection { PropSection {
title: "Table".into(), title: t!("Table").into_owned(),
props: vec![ props: vec![
ro( ro(
"Table style", t!("Table style").as_ref(),
"tbl_style_handle", "tbl_style_handle",
fmt_h(&self.table_style_handle), fmt_h(&self.table_style_handle),
), ),
ro("Rows", "tbl_rows", self.rows.len().to_string()), ro(t!("Rows").as_ref(), "tbl_rows", self.rows.len().to_string()),
ro("Columns", "tbl_cols", self.columns.len().to_string()), ro(t!("Columns").as_ref(), "tbl_cols", self.columns.len().to_string()),
ro("Contents", "tbl_contents", content_count.to_string()), ro(t!("Contents").as_ref(), "tbl_contents", content_count.to_string()),
ro( ro(
"Block contents", t!("Block contents").as_ref(),
"tbl_block_contents", "tbl_block_contents",
block_content_count.to_string(), block_content_count.to_string(),
), ),
ro( ro(
"Field contents", t!("Field contents").as_ref(),
"tbl_field_contents", "tbl_field_contents",
field_content_count.to_string(), field_content_count.to_string(),
), ),
ro( ro(
"Merged ranges", t!("Merged ranges").as_ref(),
"tbl_merged_ranges", "tbl_merged_ranges",
self.merged_ranges.len().to_string(), self.merged_ranges.len().to_string(),
), ),
ro( ro(
"Linked cells", t!("Linked cells").as_ref(),
"tbl_linked_cells", "tbl_linked_cells",
linked_cell_count.to_string(), linked_cell_count.to_string(),
), ),
ro("Direction", "tbl_direction", format!("{:.4}", direction_deg)), ro(t!("Direction").as_ref(), "tbl_direction", format!("{:.4}", direction_deg)),
ro( ro(
"Table width", t!("Table width").as_ref(),
"tbl_width", "tbl_width",
format!("{:.4}", self.total_width()), format!("{:.4}", self.total_width()),
), ),
ro( ro(
"Table height", t!("Table height").as_ref(),
"tbl_height", "tbl_height",
format!("{:.4}", self.total_height()), format!("{:.4}", self.total_height()),
), ),
], ],
}, },
PropSection { PropSection {
title: "Table Breaks".into(), title: t!("Table Breaks").into_owned(),
props: vec![ props: vec![
toggle( toggle(
"Enabled", t!("Enabled").as_ref(),
"tbl_break_enabled", "tbl_break_enabled",
self.break_options.contains(BreakOptionFlags::ENABLE_BREAKS), self.break_options.contains(BreakOptionFlags::ENABLE_BREAKS),
), ),
ro( ro(
"Direction", t!("Direction").as_ref(),
"tbl_break_direction", "tbl_break_direction",
format!("{:?}", self.break_flow_direction), format!("{:?}", self.break_flow_direction),
), ),
toggle( toggle(
"Repeat top labels", t!("Repeat top labels").as_ref(),
"tbl_break_repeat_top", "tbl_break_repeat_top",
self.break_options self.break_options
.contains(BreakOptionFlags::REPEAT_TOP_LABELS), .contains(BreakOptionFlags::REPEAT_TOP_LABELS),
), ),
toggle( toggle(
"Repeat bottom labels", t!("Repeat bottom labels").as_ref(),
"tbl_break_repeat_bottom", "tbl_break_repeat_bottom",
self.break_options self.break_options
.contains(BreakOptionFlags::REPEAT_BOTTOM_LABELS), .contains(BreakOptionFlags::REPEAT_BOTTOM_LABELS),
), ),
toggle( toggle(
"Manual positions", t!("Manual positions").as_ref(),
"tbl_break_manual_positions", "tbl_break_manual_positions",
self.break_options self.break_options
.contains(BreakOptionFlags::ALLOW_MANUAL_POSITIONS), .contains(BreakOptionFlags::ALLOW_MANUAL_POSITIONS),
), ),
toggle( toggle(
"Manual heights", t!("Manual heights").as_ref(),
"tbl_break_manual_heights", "tbl_break_manual_heights",
self.break_options self.break_options
.contains(BreakOptionFlags::ALLOW_MANUAL_HEIGHTS), .contains(BreakOptionFlags::ALLOW_MANUAL_HEIGHTS),
), ),
ro( ro(
"Segments", t!("Segments").as_ref(),
"tbl_break_segments", "tbl_break_segments",
self.break_ranges.len().max(self.break_data.len()).to_string(), self.break_ranges.len().max(self.break_data.len()).to_string(),
), ),
ro("Break heights", "tbl_break_height", break_heights), ro(t!("Break heights").as_ref(), "tbl_break_height", break_heights),
edit("Spacing", "tbl_break_spacing", self.break_spacing), edit(t!("Spacing").as_ref(), "tbl_break_spacing", self.break_spacing),
], ],
}, },
] ]

View file

@ -12,6 +12,7 @@ use crate::scene::convert::acad_to_truck::{GlyphRun, TextStroke, TruckEntity, Tr
use crate::scene::text::lff; use crate::scene::text::lff;
use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Property}; use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Property};
use crate::scene::model::wire_model::SnapHint; use crate::scene::model::wire_model::SnapHint;
use crate::t;
/// Combined single-line-text justification (horizontal × vertical) shown as one /// Combined single-line-text justification (horizontal × vertical) shown as one
/// dropdown value. Horizontal-only modes (Aligned/Middle/Fit) ignore the /// dropdown value. Horizontal-only modes (Aligned/Middle/Fit) ignore the
@ -273,15 +274,15 @@ fn properties(t: &Text, text_style_names: &[String]) -> Vec<PropSection> {
}; };
vec![ vec![
PropSection { PropSection {
title: "Text".into(), title: t!("Text").into_owned(),
props: vec![ props: vec![
Property { Property {
label: "Contents".into(), label: t!("Contents").into_owned(),
field: "content", field: "content",
value: PropValue::EditText(t.value.clone()), value: PropValue::EditText(t.value.clone()),
}, },
Property { Property {
label: "Style".into(), label: t!("Style").into_owned(),
field: "style", field: "style",
value: PropValue::Choice { value: PropValue::Choice {
selected: if t.style.trim().is_empty() { selected: if t.style.trim().is_empty() {
@ -292,9 +293,9 @@ fn properties(t: &Text, text_style_names: &[String]) -> Vec<PropSection> {
options: text_style_names.to_vec(), options: text_style_names.to_vec(),
}, },
}, },
ro("Annotative", "annotative", "No"), ro(t!("Annotative").as_ref(), "annotative", "No"),
Property { Property {
label: "Justify".into(), label: t!("Justify").into_owned(),
field: "justify", field: "justify",
value: PropValue::Choice { value: PropValue::Choice {
selected: text_justify_str( selected: text_justify_str(
@ -324,28 +325,28 @@ fn properties(t: &Text, text_style_names: &[String]) -> Vec<PropSection> {
.collect(), .collect(),
}, },
}, },
edit("Height", "height", t.height), edit(t!("Height").as_ref(), "height", t.height),
edit_angle("Rotation", "rotation", t.rotation.to_degrees()), edit_angle(t!("Rotation").as_ref(), "rotation", t.rotation.to_degrees()),
edit("Width factor", "width_factor", t.width_factor), edit(t!("Width factor").as_ref(), "width_factor", t.width_factor),
edit_angle("Obliquing", "oblique_angle", t.oblique_angle.to_degrees()), edit_angle(t!("Obliquing").as_ref(), "oblique_angle", t.oblique_angle.to_degrees()),
num_row("Text alignment X", "align_x", ax, align_editable), num_row(t!("Text alignment X").as_ref(), "align_x", ax, align_editable),
num_row("Text alignment Y", "align_y", ay, align_editable), num_row(t!("Text alignment Y").as_ref(), "align_y", ay, align_editable),
num_row("Text alignment Z", "align_z", az, align_editable), num_row(t!("Text alignment Z").as_ref(), "align_z", az, align_editable),
], ],
}, },
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
num_row("Position X", "ins_x", t.insertion_point.x, pos_editable), num_row(t!("Position X").as_ref(), "ins_x", t.insertion_point.x, pos_editable),
num_row("Position Y", "ins_y", t.insertion_point.y, pos_editable), num_row(t!("Position Y").as_ref(), "ins_y", t.insertion_point.y, pos_editable),
num_row("Position Z", "ins_z", t.insertion_point.z, pos_editable), num_row(t!("Position Z").as_ref(), "ins_z", t.insertion_point.z, pos_editable),
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
Property { Property {
label: "Upside down".into(), label: t!("Upside down").into_owned(),
field: "upside_down", field: "upside_down",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "upside_down", field: "upside_down",
@ -353,7 +354,7 @@ fn properties(t: &Text, text_style_names: &[String]) -> Vec<PropSection> {
}, },
}, },
Property { Property {
label: "Backward".into(), label: t!("Backward").into_owned(),
field: "backward", field: "backward",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "backward", field: "backward",

View file

@ -8,6 +8,7 @@ use crate::scene::model::object::{GripApply, GripDef, PropSection};
use crate::scene::model::wire_model::SnapHint; use crate::scene::model::wire_model::SnapHint;
use crate::scene::text::lff; use crate::scene::text::lff;
use crate::scene::view::transform; use crate::scene::view::transform;
use crate::t;
// ── GDT text parser ─────────────────────────────────────────────────────────── // ── GDT text parser ───────────────────────────────────────────────────────────
@ -541,25 +542,25 @@ impl PropertyEditable for Tolerance {
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> { fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
vec![ vec![
PropSection { PropSection {
title: "Text".into(), title: t!("Text").into_owned(),
props: vec![ props: vec![
ro("Text style", "tol_text_style", String::new()), ro(t!("Text style").as_ref(), "tol_text_style", String::new()),
edit("Text height", "tol_text_height", self.text_height), edit(t!("Text height").as_ref(), "tol_text_height", self.text_height),
], ],
}, },
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Position X", "tol_ix", self.insertion_point.x), edit(t!("Position X").as_ref(), "tol_ix", self.insertion_point.x),
edit("Position Y", "tol_iy", self.insertion_point.y), edit(t!("Position Y").as_ref(), "tol_iy", self.insertion_point.y),
edit("Position Z", "tol_iz", self.insertion_point.z), edit(t!("Position Z").as_ref(), "tol_iz", self.insertion_point.z),
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
ro( ro(
"Dimension style", t!("Dimension style").as_ref(),
"tol_dim_style", "tol_dim_style",
if self.dimension_style_name.is_empty() { if self.dimension_style_name.is_empty() {
"(default)".to_string() "(default)".to_string()
@ -567,9 +568,9 @@ impl PropertyEditable for Tolerance {
self.dimension_style_name.clone() self.dimension_style_name.clone()
}, },
), ),
edit("Direction X", "tol_dir_x", self.direction.x), edit(t!("Direction X").as_ref(), "tol_dir_x", self.direction.x),
edit("Direction Y", "tol_dir_y", self.direction.y), edit(t!("Direction Y").as_ref(), "tol_dir_y", self.direction.y),
edit("Direction Z", "tol_dir_z", self.direction.z), edit(t!("Direction Z").as_ref(), "tol_dir_z", self.direction.z),
], ],
}, },
] ]

View file

@ -5,6 +5,7 @@
// Props: position, scales, rotation, contrast, fade, flags. // Props: position, scales, rotation, contrast, fade, flags.
use acadrust::entities::{Underlay, UnderlayDisplayFlags}; use acadrust::entities::{Underlay, UnderlayDisplayFlags};
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{center_grip, edit_angle_prop as edit_angle, edit_prop as edit, ro_prop as ro, square_grip}; use crate::entities::common::{center_grip, edit_angle_prop as edit_angle, edit_prop as edit, ro_prop as ro, square_grip};
@ -224,26 +225,26 @@ impl PropertyEditable for Underlay {
vec![ vec![
PropSection { PropSection {
title: "Geometry".into(), title: t!("Geometry").into_owned(),
props: vec![ props: vec![
edit("Position X", "ul_ix", self.insertion_point.x), edit(t!("Position X").as_ref(), "ul_ix", self.insertion_point.x),
edit("Position Y", "ul_iy", self.insertion_point.y), edit(t!("Position Y").as_ref(), "ul_iy", self.insertion_point.y),
edit("Position Z", "ul_iz", self.insertion_point.z), edit(t!("Position Z").as_ref(), "ul_iz", self.insertion_point.z),
edit("Scale X", "ul_sx", self.x_scale), edit(t!("Scale X").as_ref(), "ul_sx", self.x_scale),
edit("Scale Y", "ul_sy", self.y_scale), edit(t!("Scale Y").as_ref(), "ul_sy", self.y_scale),
edit("Scale Z", "ul_sz", self.z_scale), edit(t!("Scale Z").as_ref(), "ul_sz", self.z_scale),
ro("Width", "ul_width", format!("{:.4}", width)), ro(t!("Width").as_ref(), "ul_width", format!("{:.4}", width)),
ro("Height", "ul_height", format!("{:.4}", height)), ro(t!("Height").as_ref(), "ul_height", format!("{:.4}", height)),
edit_angle("Rotation", "ul_rot", self.rotation.to_degrees()), edit_angle(t!("Rotation").as_ref(), "ul_rot", self.rotation.to_degrees()),
], ],
}, },
PropSection { PropSection {
title: "Underlay Adjust".into(), title: t!("Underlay Adjust").into_owned(),
props: vec![ props: vec![
edit("Contrast", "ul_contrast", self.contrast as f64), edit(t!("Contrast").as_ref(), "ul_contrast", self.contrast as f64),
edit("Fade", "ul_fade", self.fade as f64), edit(t!("Fade").as_ref(), "ul_fade", self.fade as f64),
Property { Property {
label: "Monochrome".into(), label: t!("Monochrome").into_owned(),
field: "ul_mono", field: "ul_mono",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "ul_mono", field: "ul_mono",
@ -251,7 +252,7 @@ impl PropertyEditable for Underlay {
}, },
}, },
Property { Property {
label: "Adjust Colors for Background".into(), label: t!("Adjust Colors for Background").into_owned(),
field: "ul_adjust_bg", field: "ul_adjust_bg",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "ul_adjust_bg", field: "ul_adjust_bg",
@ -261,14 +262,14 @@ impl PropertyEditable for Underlay {
], ],
}, },
PropSection { PropSection {
title: "Misc".into(), title: t!("Misc").into_owned(),
props: vec![ props: vec![
// Underlay name/path/layers live on the separate // Underlay name/path/layers live on the separate
// UnderlayDefinition object, not reachable from the entity. // UnderlayDefinition object, not reachable from the entity.
ro("Underlay name", "ul_name", String::new()), ro(t!("Underlay name").as_ref(), "ul_name", String::new()),
ro("Underlay path", "ul_path", String::new()), ro(t!("Underlay path").as_ref(), "ul_path", String::new()),
Property { Property {
label: "Show underlay".into(), label: t!("Show underlay").into_owned(),
field: "ul_on", field: "ul_on",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "ul_on", field: "ul_on",
@ -276,7 +277,7 @@ impl PropertyEditable for Underlay {
}, },
}, },
Property { Property {
label: "Clipping".into(), label: t!("Clipping").into_owned(),
field: "ul_clip", field: "ul_clip",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "ul_clip", field: "ul_clip",
@ -284,14 +285,14 @@ impl PropertyEditable for Underlay {
}, },
}, },
Property { Property {
label: "Show clipped".into(), label: t!("Show clipped").into_owned(),
field: "ul_clip_inverted", field: "ul_clip_inverted",
value: PropValue::BoolToggle { value: PropValue::BoolToggle {
field: "ul_clip_inverted", field: "ul_clip_inverted",
value: self.clip_inverted, value: self.clip_inverted,
}, },
}, },
ro("Underlay layers", "ul_layers", String::new()), ro(t!("Underlay layers").as_ref(), "ul_layers", String::new()),
], ],
}, },
] ]

View file

@ -1,4 +1,5 @@
use acadrust::entities::{SectionSymbol, ViewBorder}; use acadrust::entities::{SectionSymbol, ViewBorder};
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{edit_angle_prop, edit_prop, parse_f64, ro_prop, square_grip}; use crate::entities::common::{edit_angle_prop, edit_prop, parse_f64, ro_prop, square_grip};
@ -83,37 +84,31 @@ impl PropertyEditable for SectionSymbol {
.join("\n"); .join("\n");
vec![ vec![
PropSection { PropSection {
title: "Section Symbol".into(), title: t!("Section Symbol").into_owned(),
props: vec![ props: vec![
edit_prop("Symbol Scale", "section_scale", self.symbol_scale), edit_prop(t!("Symbol Scale").as_ref(), "section_scale", self.symbol_scale),
ro_prop( ro_prop(t!("View Symbol Version").as_ref(),
"View Symbol Version",
"section_view_version", "section_view_version",
self.view_symbol_version.to_string(), self.view_symbol_version.to_string(),
), ),
ro_prop("Version", "section_version", self.version.to_string()), ro_prop(t!("Version").as_ref(), "section_version", self.version.to_string()),
ro_prop( ro_prop(t!("Style").as_ref(),
"Style",
"section_style", "section_style",
handle_text(self.style_handle), handle_text(self.style_handle),
), ),
ro_prop( ro_prop(t!("View Representation").as_ref(),
"View Representation",
"section_view_rep", "section_view_rep",
handle_text(self.view_rep_handle), handle_text(self.view_rep_handle),
), ),
ro_prop( ro_prop(t!("View Symbol Flags").as_ref(),
"View Symbol Flags",
"section_view_flags", "section_view_flags",
self.raw_view_symbol_70.to_string(), self.raw_view_symbol_70.to_string(),
), ),
ro_prop( ro_prop(t!("Point Count").as_ref(),
"Point Count",
"section_point_count", "section_point_count",
self.points.len().to_string(), self.points.len().to_string(),
), ),
ro_prop( ro_prop(t!("Stored Point Counts").as_ref(),
"Stored Point Counts",
"section_raw_counts", "section_raw_counts",
format!( format!(
"{}, {}, flags {}", "{}, {}, flags {}",
@ -122,12 +117,12 @@ impl PropertyEditable for SectionSymbol {
self.raw_flags_90 self.raw_flags_90
), ),
), ),
ro_prop("Identifier", "section_label", self.label.clone()), ro_prop(t!("Identifier").as_ref(), "section_label", self.label.clone()),
], ],
}, },
PropSection { PropSection {
title: "Section Points".into(), title: t!("Section Points").into_owned(),
props: vec![ro_prop("Records", "section_points", point_data)], props: vec![ro_prop(t!("Records").as_ref(), "section_points", point_data)],
}, },
] ]
} }
@ -197,34 +192,29 @@ impl Grippable for ViewBorder {
impl PropertyEditable for ViewBorder { impl PropertyEditable for ViewBorder {
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> { fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
vec![PropSection { vec![PropSection {
title: "Drawing View".into(), title: t!("Drawing View").into_owned(),
props: vec![ props: vec![
edit_prop("Center X", "view_border_x", self.center[0]), edit_prop(t!("Center X").as_ref(), "view_border_x", self.center[0]),
edit_prop("Center Y", "view_border_y", self.center[1]), edit_prop(t!("Center Y").as_ref(), "view_border_y", self.center[1]),
ro_prop( ro_prop(t!("Width").as_ref(),
"Width",
"view_border_width", "view_border_width",
format!("{:.6}", self.max[0] - self.min[0]), format!("{:.6}", self.max[0] - self.min[0]),
), ),
ro_prop( ro_prop(t!("Height").as_ref(),
"Height",
"view_border_height", "view_border_height",
format!("{:.6}", self.max[1] - self.min[1]), format!("{:.6}", self.max[1] - self.min[1]),
), ),
edit_prop("Scale", "view_border_scale", self.scale), edit_prop(t!("Scale").as_ref(), "view_border_scale", self.scale),
edit_angle_prop( edit_angle_prop(t!("Rotation").as_ref(),
"Rotation",
"view_border_rotation", "view_border_rotation",
self.rotation_angle.to_degrees(), self.rotation_angle.to_degrees(),
), ),
ro_prop("Version", "view_border_version", self.version.to_string()), ro_prop(t!("Version").as_ref(), "view_border_version", self.version.to_string()),
ro_prop( ro_prop(t!("Active Viewport").as_ref(),
"Active Viewport",
"view_border_viewport", "view_border_viewport",
handle_text(self.active_viewport), handle_text(self.active_viewport),
), ),
ro_prop( ro_prop(t!("Scale Object").as_ref(),
"Scale Object",
"view_border_scale_handle", "view_border_scale_handle",
handle_text(self.scale_handle), handle_text(self.scale_handle),
), ),

View file

@ -7,9 +7,13 @@ use i18n_embed::DesktopLanguageRequester;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use i18n_embed::WebLanguageRequester; use i18n_embed::WebLanguageRequester;
use rust_embed::RustEmbed; use rust_embed::RustEmbed;
use std::borrow::Cow;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::OnceLock; use std::sync::OnceLock;
#[path = "locale_catalog.rs"]
mod locale_catalog;
#[derive(RustEmbed)] #[derive(RustEmbed)]
#[folder = "locales/"] #[folder = "locales/"]
struct Localizations; struct Localizations;
@ -123,6 +127,107 @@ pub fn set_language(language: Language) -> Result<(), i18n_embed::I18nEmbedError
load_language(loader(), language) load_language(loader(), language)
} }
/// Translate an application-facing source label from the complete UI catalog.
///
/// Stable semantic ids remain preferable for new code. This compatibility
/// layer lets existing UI surfaces move to Fluent without turning command
/// tokens, property ids, file-format values, or plug-in supplied text into
/// translatable data.
pub fn translate(source: impl AsRef<str>) -> Cow<'static, str> {
let source = source.as_ref();
locale_catalog::message_id(source)
.map(|message_id| Cow::Owned(loader().get(message_id)))
.unwrap_or_else(|| Cow::Owned(source.to_string()))
}
/// Translate a catalog message and replace the named values used by legacy
/// command prompts. Catalog generation protects these markers from machine
/// translation and Fluent parsing.
pub fn translate_args(
source: impl AsRef<str>,
args: &[(&str, String)],
) -> Cow<'static, str> {
let source = source.as_ref();
let mut translated = locale_catalog::message_id(source)
.map(|message_id| loader().get(message_id))
.unwrap_or_else(|| source.to_string());
for (name, value) in args {
translated = translated.replace(&format!("__ocs_arg_{name}__"), value);
translated = translated.replace(&format!("%{{{name}}}"), value);
}
Cow::Owned(translated)
}
/// Translate a Rust formatting template after its values have been rendered.
/// The catalog stores positional markers, while this function recovers each
/// rendered value from the English template and places it in the localized
/// sentence. File names, handles, counts, and command values therefore remain
/// data instead of being sent through translation.
pub fn translate_format(template: &str, rendered: String) -> Cow<'static, str> {
let Some(message_id) = locale_catalog::message_id(template) else {
return Cow::Owned(rendered);
};
let Some(values) = format_values(template, &rendered) else {
return Cow::Owned(rendered);
};
let mut translated = loader().get(message_id);
for (index, value) in values.iter().enumerate() {
translated = translated.replace(&format!("__ocs_fmt_{index}__"), value);
}
Cow::Owned(translated)
}
fn format_values(template: &str, rendered: &str) -> Option<Vec<String>> {
let mut literals = vec![String::new()];
let mut chars = template.chars().peekable();
while let Some(ch) = chars.next() {
match (ch, chars.peek().copied()) {
('{', Some('{')) => {
chars.next();
literals.last_mut()?.push('{');
}
('}', Some('}')) => {
chars.next();
literals.last_mut()?.push('}');
}
('{', _) => {
let mut closed = false;
for inner in chars.by_ref() {
if inner == '}' {
closed = true;
break;
}
}
if !closed {
return None;
}
literals.push(String::new());
}
('}', _) => return None,
_ => literals.last_mut()?.push(ch),
}
}
let mut cursor = 0;
if !rendered.starts_with(&literals[0]) {
return None;
}
cursor += literals[0].len();
let mut values = Vec::with_capacity(literals.len().saturating_sub(1));
for index in 1..literals.len() {
let separator = &literals[index];
if index + 1 == literals.len() && separator.is_empty() {
values.push(rendered[cursor..].to_string());
cursor = rendered.len();
} else {
let offset = rendered[cursor..].find(separator)?;
values.push(rendered[cursor..cursor + offset].to_string());
cursor += offset + separator.len();
}
}
(cursor == rendered.len()).then_some(values)
}
/// Built-in ribbon modules have stable ids; plug-ins keep their supplied title /// Built-in ribbon modules have stable ids; plug-ins keep their supplied title
/// until they provide their own localization bundle. /// until they provide their own localization bundle.
pub fn ribbon_module_title(id: &str, fallback: &str) -> String { pub fn ribbon_module_title(id: &str, fallback: &str) -> String {
@ -151,3 +256,28 @@ macro_rules! tr {
) )
}; };
} }
/// Source-catalog compatibility macro used while the existing interface is
/// migrated to semantic Fluent ids. Named arguments mirror the command prompt
/// placeholders already present in the source catalog.
#[macro_export]
macro_rules! t {
($source:expr $(,)?) => {
$crate::i18n::translate($source)
};
($source:expr, $($name:ident = $value:expr),+ $(,)?) => {
$crate::i18n::translate_args(
$source,
&[$((stringify!($name), ($value).to_string())),+],
)
};
}
/// Localized counterpart of `format!` for application-facing messages.
#[macro_export]
macro_rules! tf {
($template:literal $($args:tt)*) => {{
let rendered = format!($template $($args)*);
$crate::i18n::translate_format($template, rendered)
}};
}

2987
src/locale_catalog.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -8,6 +8,7 @@ use glam::DVec3;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_aligned.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_aligned.svg"));
@ -57,18 +58,18 @@ impl CadCommand for AlignedDimensionCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
if self.awaiting_text { if self.awaiting_text {
return "DIMALIGNED Enter dimension text (blank = measured value):".into(); return t!("DIMALIGNED Enter dimension text (blank = measured value):").into_owned();
} }
if self.awaiting_angle { if self.awaiting_angle {
return "DIMALIGNED Specify text angle (degrees):".into(); return t!("DIMALIGNED Specify text angle (degrees):").into_owned();
} }
match self.step { match self.step {
Step::First => "DIMALIGNED Specify first extension line origin:".into(), Step::First => t!("DIMALIGNED Specify first extension line origin:").into_owned(),
Step::Second(_) => { Step::Second(_) => {
"DIMALIGNED Specify second extension line origin [Text/Angle]:".into() t!("DIMALIGNED Specify second extension line origin [Text/Angle]:").into_owned()
} }
Step::DimLine { .. } => { Step::DimLine { .. } => {
"DIMALIGNED Specify dimension line location [Text/Angle]:".into() t!("DIMALIGNED Specify dimension line location [Text/Angle]:").into_owned()
} }
} }
} }

View file

@ -6,6 +6,7 @@ use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use glam::{DVec3, Vec3}; use glam::{DVec3, Vec3};
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_angular.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_angular.svg"));
@ -49,10 +50,14 @@ impl CadCommand for AngularDimensionCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.step { match self.step {
Step::Vertex => "DIMANGULAR Specify angle vertex:".into(), Step::Vertex => t!("DIMANGULAR Specify angle vertex:").into_owned(),
Step::FirstRay(_) => "DIMANGULAR Specify first extension line point:".into(), Step::FirstRay(_) => {
Step::SecondRay { .. } => "DIMANGULAR Specify second extension line point:".into(), t!("DIMANGULAR Specify first extension line point:").into_owned()
Step::ArcPoint { .. } => "DIMANGULAR Specify dimension arc location:".into(), }
Step::SecondRay { .. } => {
t!("DIMANGULAR Specify second extension line point:").into_owned()
}
Step::ArcPoint { .. } => t!("DIMANGULAR Specify dimension arc location:").into_owned(),
} }
} }

View file

@ -12,6 +12,7 @@ use glam::DVec3;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/ddedit.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/ddedit.svg"));
@ -38,7 +39,7 @@ impl CadCommand for DdeditCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
"DDEDIT Select text entity:".into() t!("DDEDIT Select text entity:").into_owned()
} }
fn needs_entity_pick(&self) -> bool { fn needs_entity_pick(&self) -> bool {

View file

@ -8,6 +8,7 @@ use glam::{DVec3, Vec3};
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_diameter.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_diameter.svg"));
@ -57,16 +58,16 @@ impl CadCommand for DiameterDimensionCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
if self.awaiting_text { if self.awaiting_text {
return "DIMDIAMETER Enter dimension text (blank = measured value):".into(); return t!("DIMDIAMETER Enter dimension text (blank = measured value):").into_owned();
} }
if self.awaiting_angle { if self.awaiting_angle {
return "DIMDIAMETER Specify text angle (degrees):".into(); return t!("DIMDIAMETER Specify text angle (degrees):").into_owned();
} }
match self.step { match self.step {
Step::CenterPoint => "DIMDIAMETER Specify center point:".into(), Step::CenterPoint => t!("DIMDIAMETER Specify center point:").into_owned(),
Step::ArcPoint(_) => "DIMDIAMETER Specify point on circle:".into(), Step::ArcPoint(_) => t!("DIMDIAMETER Specify point on circle:").into_owned(),
Step::TextPoint { .. } => { Step::TextPoint { .. } => {
"DIMDIAMETER Specify dimension line location [Text/Angle]:".into() t!("DIMDIAMETER Specify dimension line location [Text/Angle]:").into_owned()
} }
} }
} }

View file

@ -14,6 +14,7 @@ use glam::{DVec3, Vec3};
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_baseline.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_baseline.svg"));
@ -130,9 +131,9 @@ impl CadCommand for DimBaselineCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
if !self.ready { if !self.ready {
"DIMBASELINE No base dimension found. Place a dimension first.".into() t!("DIMBASELINE No base dimension found. Place a dimension first.").into_owned()
} else { } else {
"DIMBASELINE Specify a second extension line origin (Enter to exit):".into() t!("DIMBASELINE Specify a second extension line origin (Enter to exit):").into_owned()
} }
} }

View file

@ -14,6 +14,7 @@ use glam::{DVec3, Vec3};
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_continue.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_continue.svg"));
@ -104,9 +105,9 @@ impl CadCommand for DimContinueCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
if !self.ready { if !self.ready {
"DIMCONTINUE No base dimension found. Place a dimension first.".into() t!("DIMCONTINUE No base dimension found. Place a dimension first.").into_owned()
} else { } else {
"DIMCONTINUE Specify a second extension line origin (Enter to exit):".into() t!("DIMCONTINUE Specify a second extension line origin (Enter to exit):").into_owned()
} }
} }

View file

@ -14,6 +14,7 @@ use glam::DVec3;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_break.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_break.svg"));
@ -50,9 +51,9 @@ impl CadCommand for DimBreakCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
Step::PickDim => "DIMBREAK Select dimension to break:".into(), Step::PickDim => t!("DIMBREAK Select dimension to break:").into_owned(),
Step::PickCrossing { .. } => { Step::PickCrossing { .. } => {
"DIMBREAK Select object to break at, or Enter for Auto:".into() t!("DIMBREAK Select object to break at, or Enter for Auto:").into_owned()
} }
} }
} }

View file

@ -9,6 +9,7 @@ use glam::DVec3;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_edit.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_edit.svg"));
@ -45,9 +46,9 @@ impl CadCommand for DimEditCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
Step::PickDim => "DIMEDIT Select dimension:".into(), Step::PickDim => t!("DIMEDIT Select dimension:").into_owned(),
Step::EnterText { .. } => { Step::EnterText { .. } => {
"DIMEDIT Enter text override (blank = reset to measured):".into() t!("DIMEDIT Enter text override (blank = reset to measured):").into_owned()
} }
} }
} }

View file

@ -10,6 +10,7 @@ use glam::DVec3;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_jog.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_jog.svg"));
@ -46,8 +47,8 @@ impl CadCommand for DimJogLineCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
Step::PickDim => "DIMJOGLINE Select linear or aligned dimension:".into(), Step::PickDim => t!("DIMJOGLINE Select linear or aligned dimension:").into_owned(),
Step::PickJogPos { .. } => "DIMJOGLINE Specify jog location:".into(), Step::PickJogPos { .. } => t!("DIMJOGLINE Specify jog location:").into_owned(),
} }
} }

View file

@ -10,6 +10,7 @@ use glam::DVec3;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_space.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_space.svg"));
@ -47,12 +48,13 @@ impl CadCommand for DimSpaceCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
Step::PickBase => "DIMSPACE Select base dimension:".into(), Step::PickBase => t!("DIMSPACE Select base dimension:").into_owned(),
Step::PickOthers { others, .. } => format!( Step::PickOthers { others, .. } => t!(
"DIMSPACE Select dimension to space ({} selected, Enter when done):", "DIMSPACE Select dimension to space (%{count} selected, Enter when done):",
others.len() count = others.len()
), )
Step::EnterSpacing { .. } => "DIMSPACE Enter value (0 = auto):".into(), .into_owned(),
Step::EnterSpacing { .. } => t!("DIMSPACE Enter value (0 = auto):").into_owned(),
} }
} }

View file

@ -10,6 +10,7 @@ use glam::DVec3;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_tedit.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_tedit.svg"));
@ -49,8 +50,10 @@ impl CadCommand for DimTeditCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
Step::PickDim => "DIMTEDIT Select dimension:".into(), Step::PickDim => t!("DIMTEDIT Select dimension:").into_owned(),
Step::PickTextPos { .. } => "DIMTEDIT Specify new location for dimension text:".into(), Step::PickTextPos { .. } => {
t!("DIMTEDIT Specify new location for dimension text:").into_owned()
}
} }
} }

View file

@ -18,6 +18,7 @@ use glam::{DVec3, Mat4, Vec3};
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/leader.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/leader.svg"));
@ -52,12 +53,13 @@ impl CadCommand for LeaderCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
if self.verts.is_empty() { if self.verts.is_empty() {
"LEADER Specify arrowhead point:".into() t!("LEADER Specify arrowhead point:").into_owned()
} else { } else {
format!( t!(
"LEADER Specify next point [{} pts — Enter to place text]:", "LEADER Specify next point [%{count} pts — Enter to place text]:",
self.verts.len() count = self.verts.len()
) )
.into_owned()
} }
} }

View file

@ -6,6 +6,7 @@ use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use glam::{DVec3, Mat4, Vec3}; use glam::{DVec3, Mat4, Vec3};
use crate::t;
/// World-space measurement axis for a linear dimension between `first` and /// World-space measurement axis for a linear dimension between `first` and
/// `second`, chosen as the UCS X or Y axis (whichever the span is closer to). /// `second`, chosen as the UCS X or Y axis (whichever the span is closer to).
@ -75,18 +76,18 @@ impl CadCommand for LinearDimensionCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
if self.awaiting_text { if self.awaiting_text {
return "DIMLINEAR Enter dimension text (blank = measured value):".into(); return t!("DIMLINEAR Enter dimension text (blank = measured value):").into_owned();
} }
if self.awaiting_angle { if self.awaiting_angle {
return "DIMLINEAR Specify text angle (degrees):".into(); return t!("DIMLINEAR Specify text angle (degrees):").into_owned();
} }
match self.step { match self.step {
Step::FirstPoint => "DIMLINEAR Specify first extension line origin:".into(), Step::FirstPoint => t!("DIMLINEAR Specify first extension line origin:").into_owned(),
Step::SecondPoint(_) => { Step::SecondPoint(_) => {
"DIMLINEAR Specify second extension line origin [Text/Angle]:".into() t!("DIMLINEAR Specify second extension line origin [Text/Angle]:").into_owned()
} }
Step::DimensionLine { .. } => { Step::DimensionLine { .. } => {
"DIMLINEAR Specify dimension line location [Text/Angle]:".into() t!("DIMLINEAR Specify dimension line location [Text/Angle]:").into_owned()
} }
} }
} }

View file

@ -13,6 +13,7 @@ use glam::{DVec3, Mat4, Vec3};
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/mleader.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/mleader.svg"));
@ -47,12 +48,13 @@ impl CadCommand for MLeaderCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
if self.verts.is_empty() { if self.verts.is_empty() {
"MLEADER Specify arrowhead point:".into() t!("MLEADER Specify arrowhead point:").into_owned()
} else { } else {
format!( t!(
"MLEADER Specify next point [{} pts — Enter to place text]:", "MLEADER Specify next point [%{count} pts — Enter to place text]:",
self.verts.len() count = self.verts.len()
) )
.into_owned()
} }
} }

View file

@ -13,6 +13,7 @@ use glam::{DVec3, Vec3};
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use crate::t;
// ── MLEADERADD ──────────────────────────────────────────────────────────────── // ── MLEADERADD ────────────────────────────────────────────────────────────────
@ -60,12 +61,15 @@ impl CadCommand for MLeaderAddCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
AddStep::PickMLeader => "MLEADERADD Select a multileader:".into(), AddStep::PickMLeader => t!("MLEADERADD Select a multileader:").into_owned(),
AddStep::PickArrowhead { .. } => "MLEADERADD Specify arrowhead location:".into(), AddStep::PickArrowhead { .. } => {
AddStep::CollectPoints { pts, .. } => format!( t!("MLEADERADD Specify arrowhead location:").into_owned()
"MLEADERADD Specify next leader point ({} pts, Enter to finish):", }
pts.len() AddStep::CollectPoints { pts, .. } => t!(
), "MLEADERADD Specify next leader point (%{count} pts, Enter to finish):",
count = pts.len()
)
.into_owned(),
} }
} }
@ -192,9 +196,9 @@ impl CadCommand for MLeaderRemoveCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
RemoveStep::PickMLeader => "MLEADERREMOVE Select a multileader:".into(), RemoveStep::PickMLeader => t!("MLEADERREMOVE Select a multileader:").into_owned(),
RemoveStep::PickLeaderToRemove { .. } => { RemoveStep::PickLeaderToRemove { .. } => {
"MLEADERREMOVE Click near the leader line to remove:".into() t!("MLEADERREMOVE Click near the leader line to remove:").into_owned()
} }
} }
} }
@ -300,13 +304,13 @@ impl CadCommand for MLeaderAlignCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
AlignStep::Gathering => { AlignStep::Gathering => {
"MLEADERALIGN Select multileaders to align (Enter when done):".into() t!("MLEADERALIGN Select multileaders to align (Enter when done):").into_owned()
} }
AlignStep::PickDirection { .. } => { AlignStep::PickDirection { .. } => {
"MLEADERALIGN Specify direction — pick start point:".into() t!("MLEADERALIGN Specify direction — pick start point:").into_owned()
} }
AlignStep::PickEndDir { .. } => { AlignStep::PickEndDir { .. } => {
"MLEADERALIGN Specify end point of alignment direction:".into() t!("MLEADERALIGN Specify end point of alignment direction:").into_owned()
} }
} }
} }
@ -405,10 +409,10 @@ impl CadCommand for MLeaderCollectCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
CollectStep::Gathering => { CollectStep::Gathering => {
"MLEADERCOLLECT Select multileaders to collect (Enter when done):".into() t!("MLEADERCOLLECT Select multileaders to collect (Enter when done):").into_owned()
} }
CollectStep::PickLocation { .. } => { CollectStep::PickLocation { .. } => {
"MLEADERCOLLECT Specify collected multileader location:".into() t!("MLEADERCOLLECT Specify collected multileader location:").into_owned()
} }
} }
} }

View file

@ -2,6 +2,7 @@ use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use glam::DVec3; use glam::DVec3;
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/mtext.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/mtext.svg"));
@ -37,7 +38,7 @@ impl CadCommand for MTextCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
Step::InsertPoint => "MTEXT Specify insertion point:".into(), Step::InsertPoint => t!("MTEXT Specify insertion point:").into_owned(),
} }
} }

View file

@ -16,6 +16,7 @@ use glam::{DVec3, Vec3};
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use crate::t;
pub fn tool() -> ToolDef { pub fn tool() -> ToolDef {
ToolDef { ToolDef {
@ -50,8 +51,8 @@ impl CadCommand for OrdinateDimCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.step { match self.step {
Step::FeaturePoint => "DIMORDINATE Specify feature location:".into(), Step::FeaturePoint => t!("DIMORDINATE Specify feature location:").into_owned(),
Step::LeaderEndpoint { .. } => "DIMORDINATE Specify leader endpoint:".into(), Step::LeaderEndpoint { .. } => t!("DIMORDINATE Specify leader endpoint:").into_owned(),
} }
} }

View file

@ -14,6 +14,7 @@ use glam::DVec3;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/qdim.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/qdim.svg"));
@ -50,8 +51,10 @@ impl CadCommand for QdimCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
Step::Gathering => "QDIM Select geometry to dimension (Enter when done):".into(), Step::Gathering => {
Step::PlaceLine { .. } => "QDIM Specify dimension line position:".into(), t!("QDIM Select geometry to dimension (Enter when done):").into_owned()
}
Step::PlaceLine { .. } => t!("QDIM Specify dimension line position:").into_owned(),
} }
} }

View file

@ -6,6 +6,7 @@ use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use glam::{DVec3, Vec3}; use glam::{DVec3, Vec3};
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_radius.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_radius.svg"));
@ -55,16 +56,16 @@ impl CadCommand for RadiusDimensionCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
if self.awaiting_text { if self.awaiting_text {
return "DIMRADIUS Enter dimension text (blank = measured value):".into(); return t!("DIMRADIUS Enter dimension text (blank = measured value):").into_owned();
} }
if self.awaiting_angle { if self.awaiting_angle {
return "DIMRADIUS Specify text angle (degrees):".into(); return t!("DIMRADIUS Specify text angle (degrees):").into_owned();
} }
match self.step { match self.step {
Step::CenterPoint => "DIMRADIUS Specify center point:".into(), Step::CenterPoint => t!("DIMRADIUS Specify center point:").into_owned(),
Step::RadiusPoint(_) => "DIMRADIUS Specify radius point:".into(), Step::RadiusPoint(_) => t!("DIMRADIUS Specify radius point:").into_owned(),
Step::TextPoint { .. } => { Step::TextPoint { .. } => {
"DIMRADIUS Specify dimension line location [Text/Angle]:".into() t!("DIMRADIUS Specify dimension line location [Text/Angle]:").into_owned()
} }
} }
} }

View file

@ -15,6 +15,7 @@ use glam::DVec3;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/table.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/table.svg"));
@ -57,13 +58,23 @@ impl CadCommand for TableCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
Step::Columns => format!("TABLE Enter number of columns [{DEFAULT_COLS}]:"), Step::Columns => t!(
Step::Rows { cols } => format!( "TABLE Enter number of columns [%{cols}]:",
"TABLE Enter number of rows (incl. header) [{DEFAULT_ROWS}] ({cols} cols):" cols = DEFAULT_COLS
), )
Step::Insertion { cols, rows } => { .into_owned(),
format!("TABLE Specify insertion point [{cols}×{rows}]:") Step::Rows { cols } => t!(
} "TABLE Enter number of rows (incl. header) [%{rows}] (%{cols} cols):",
rows = DEFAULT_ROWS,
cols = cols
)
.into_owned(),
Step::Insertion { cols, rows } => t!(
"TABLE Specify insertion point [%{cols}×%{rows}]:",
cols = cols,
rows = rows
)
.into_owned(),
} }
} }

View file

@ -2,6 +2,7 @@ use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use glam::DVec3; use glam::DVec3;
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/text.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/text.svg"));
@ -37,7 +38,7 @@ impl CadCommand for TextCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
Step::InsertPoint => "TEXT Specify insertion point:".into(), Step::InsertPoint => t!("TEXT Specify insertion point:").into_owned(),
} }
} }

View file

@ -11,6 +11,7 @@ use acadrust::Handle;
use glam::DVec3; use glam::DVec3;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::t;
/// Parse a TEXTEDITMODE value. Accepts `0`/`m`/`multiple`/`false` (Multiple → false) /// Parse a TEXTEDITMODE value. Accepts `0`/`m`/`multiple`/`false` (Multiple → false)
/// and `1`/`s`/`single`/`true` (Single → true), case-insensitive. Returns `None` /// and `1`/`s`/`single`/`true` (Single → true), case-insensitive. Returns `None`
@ -69,24 +70,27 @@ impl CadCommand for TexteditCommand {
match self.step { match self.step {
Step::PickObject => { Step::PickObject => {
if self.edit_count == 0 { if self.edit_count == 0 {
"TEXTEDIT Select an annotation object or [Undo Mode]:".to_string() t!("TEXTEDIT Select an annotation object or [Undo Mode]:").into_owned()
} else { } else {
"TEXTEDIT Select an annotation object or [Undo Mode] <exit>:".to_string() t!("TEXTEDIT Select an annotation object or [Undo Mode] <exit>:").into_owned()
} }
} }
Step::EnterMode => { Step::EnterMode => {
let prefix = if self.invalid_mode { let prefix = if self.invalid_mode {
"Requires Single or Multiple. " format!("{} ", t!("Requires Single or Multiple."))
} else { } else {
"" String::new()
}; };
format!( let mode = match self.mode {
"{prefix}TEXTEDIT Enter text edit mode [Single/Multiple] <{}>:", TextEditMode::Single => t!("Single"),
match self.mode { TextEditMode::Multiple => t!("Multiple"),
TextEditMode::Single => "Single", };
TextEditMode::Multiple => "Multiple", t!(
} "%{prefix}TEXTEDIT Enter text edit mode [Single/Multiple] <%{mode}>:",
prefix = prefix,
mode = mode
) )
.into_owned()
} }
} }
} }
@ -220,11 +224,16 @@ impl CadCommand for TexteditmodeCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
let v = if self.current { 1 } else { 0 }; let v = if self.current { 1 } else { 0 };
let prefix = if self.invalid { let prefix = if self.invalid {
"Requires 0 OR 1 OR MULTIPLE OR SINGLE. " format!("{} ", t!("Requires 0 OR 1 OR MULTIPLE OR SINGLE."))
} else { } else {
"" String::new()
}; };
format!("{prefix}TEXTEDITMODE Enter new value for TEXTEDITMODE <{v}>:") t!(
"%{prefix}TEXTEDITMODE Enter new value for TEXTEDITMODE <%{v}>:",
prefix = prefix,
v = v
)
.into_owned()
} }
fn wants_text_input(&self) -> bool { fn wants_text_input(&self) -> bool {

View file

@ -12,6 +12,7 @@ use glam::DVec3;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/tolerance.svg")); pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/tolerance.svg"));
@ -46,8 +47,10 @@ impl CadCommand for ToleranceCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
Step::Text => "TOLERANCE Enter tolerance text:".into(), Step::Text => t!("TOLERANCE Enter tolerance text:").into_owned(),
Step::Insertion { text } => format!("TOLERANCE Specify insertion point [{text}]:"), Step::Insertion { text } => {
t!("TOLERANCE Specify insertion point [%{text}]:", text = text).into_owned()
}
} }
} }

View file

@ -4,6 +4,7 @@
// receives the dispatched `COPYBASE_AT <x> <y> <z>` token. // receives the dispatched `COPYBASE_AT <x> <y> <z>` token.
use glam::DVec3; use glam::DVec3;
use crate::t;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
@ -21,7 +22,7 @@ impl CadCommand for CopyBaseCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
"COPYBASE Specify base point:".to_string() t!("COPYBASE Specify base point:").into_owned()
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, pt: DVec3) -> CmdResult {

View file

@ -17,6 +17,7 @@ pub const MENU_ITEMS: &[(&str, &str, IconKind)] = &[
use acadrust::Handle; use acadrust::Handle;
use glam::{DVec3, Vec3}; use glam::{DVec3, Vec3};
use crate::t;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
@ -103,7 +104,7 @@ impl CadCommand for PasteCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
"PASTECLIP Pick insertion point:".into() t!("PASTECLIP Pick insertion point:").into_owned()
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, pt: DVec3) -> CmdResult {

View file

@ -15,6 +15,7 @@
use acadrust::types::Vector3; use acadrust::types::Vector3;
use acadrust::{Arc as CadArc, EntityType}; use acadrust::{Arc as CadArc, EntityType};
use crate::t;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::IconKind; use crate::modules::IconKind;
@ -312,12 +313,20 @@ impl CadCommand for ArcCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.step { match self.step {
0 => "ARC Specify center:".into(), 0 => t!("ARC Specify center:").into_owned(),
1 => "ARC Specify start point:".into(), 1 => t!("ARC Specify start point:").into_owned(),
_ => format!( _ => {
"ARC Specify end point [c=({:.2},{:.2}) r={:.3}]:", let cx = format!("{:.2}", self.c.x);
self.c.x, self.c.y, self.r let cy = format!("{:.2}", self.c.y);
), let r = format!("{:.3}", self.r);
t!(
"ARC Specify end point [c=(%{cx},%{cy}) r=%{r}]:",
cx = cx,
cy = cy,
r = r
)
.into_owned()
}
} }
} }
@ -437,9 +446,9 @@ impl CadCommand for Arc3PCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.pts.len() { match self.pts.len() {
0 => "ARC 3P Specify start point:".into(), 0 => t!("ARC 3P Specify start point:").into_owned(),
1 => "ARC 3P Specify second point on arc:".into(), 1 => t!("ARC 3P Specify second point on arc:").into_owned(),
_ => "ARC 3P Specify end point:".into(), _ => t!("ARC 3P Specify end point:").into_owned(),
} }
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, pt: DVec3) -> CmdResult {
@ -537,9 +546,12 @@ impl CadCommand for ArcSCECommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.step { match self.step {
0 => "ARC SCE Specify start point:".into(), 0 => t!("ARC SCE Specify start point:").into_owned(),
1 => "ARC SCE Specify center:".into(), 1 => t!("ARC SCE Specify center:").into_owned(),
_ => format!("ARC SCE Specify end point [r={:.3}]:", self.r), _ => {
let r = format!("{:.3}", self.r);
t!("ARC SCE Specify end point [r=%{r}]:", r = r).into_owned()
}
} }
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, pt: DVec3) -> CmdResult {
@ -637,12 +649,16 @@ impl CadCommand for ArcSCACommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.step { match self.step {
0 => "ARC SCA Specify start point:".into(), 0 => t!("ARC SCA Specify start point:").into_owned(),
1 => "ARC SCA Specify center:".into(), 1 => t!("ARC SCA Specify center:").into_owned(),
_ => format!( _ => {
"ARC SCA Click end direction or type arc span in degrees [start={:.1}°]:", let sa = format!("{:.1}°", self.sa.to_degrees());
self.sa.to_degrees() t!(
), "ARC SCA Click end direction or type arc span in degrees [start=%{sa}]:",
sa = sa
)
.into_owned()
}
} }
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, pt: DVec3) -> CmdResult {
@ -767,12 +783,16 @@ impl CadCommand for ArcSCLCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.step { match self.step {
0 => "ARC SCL Specify start point:".into(), 0 => t!("ARC SCL Specify start point:").into_owned(),
1 => "ARC SCL Specify center:".into(), 1 => t!("ARC SCL Specify center:").into_owned(),
_ => format!( _ => {
"ARC SCL Click chord end or type chord length [r={:.3}]:", let r = format!("{:.3}", self.r);
self.r t!(
), "ARC SCL Click chord end or type chord length [r=%{r}]:",
r = r
)
.into_owned()
}
} }
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, pt: DVec3) -> CmdResult {
@ -871,9 +891,9 @@ impl CadCommand for ArcSEACommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.step { match self.step {
0 => "ARC SEA Specify start point:".into(), 0 => t!("ARC SEA Specify start point:").into_owned(),
1 => "ARC SEA Specify end point:".into(), 1 => t!("ARC SEA Specify end point:").into_owned(),
_ => "ARC SEA Specify angle (move cursor perpendicular to chord):".into(), _ => t!("ARC SEA Specify angle (move cursor perpendicular to chord):").into_owned(),
} }
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, pt: DVec3) -> CmdResult {
@ -949,9 +969,9 @@ impl CadCommand for ArcSERCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.step { match self.step {
0 => "ARC SER Specify start point:".into(), 0 => t!("ARC SER Specify start point:").into_owned(),
1 => "ARC SER Specify end point:".into(), 1 => t!("ARC SER Specify end point:").into_owned(),
_ => "ARC SER Click radius point or type radius value:".into(), _ => t!("ARC SER Click radius point or type radius value:").into_owned(),
} }
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, pt: DVec3) -> CmdResult {
@ -1065,9 +1085,9 @@ impl CadCommand for ArcSEDCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.step { match self.step {
0 => "ARC SED Specify start point:".into(), 0 => t!("ARC SED Specify start point:").into_owned(),
1 => "ARC SED Specify end point:".into(), 1 => t!("ARC SED Specify end point:").into_owned(),
_ => "ARC SED Specify tangent direction at start:".into(), _ => t!("ARC SED Specify tangent direction at start:").into_owned(),
} }
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, pt: DVec3) -> CmdResult {
@ -1149,12 +1169,16 @@ impl CadCommand for ArcCSACommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.step { match self.step {
0 => "ARC CSA Specify center:".into(), 0 => t!("ARC CSA Specify center:").into_owned(),
1 => "ARC CSA Specify start point:".into(), 1 => t!("ARC CSA Specify start point:").into_owned(),
_ => format!( _ => {
"ARC CSA Click end direction or type arc span in degrees [start={:.1}°]:", let sa = format!("{:.1}°", self.sa.to_degrees());
self.sa.to_degrees() t!(
), "ARC CSA Click end direction or type arc span in degrees [start=%{sa}]:",
sa = sa
)
.into_owned()
}
} }
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, pt: DVec3) -> CmdResult {
@ -1274,12 +1298,16 @@ impl CadCommand for ArcCSLCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.step { match self.step {
0 => "ARC CSL Specify center:".into(), 0 => t!("ARC CSL Specify center:").into_owned(),
1 => "ARC CSL Specify start point:".into(), 1 => t!("ARC CSL Specify start point:").into_owned(),
_ => format!( _ => {
"ARC CSL Click chord end or type chord length [r={:.3}]:", let r = format!("{:.3}", self.r);
self.r t!(
), "ARC CSL Click chord end or type chord length [r=%{r}]:",
r = r
)
.into_owned()
}
} }
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, pt: DVec3) -> CmdResult {
@ -1386,7 +1414,7 @@ impl CadCommand for ArcContCommand {
"ARC_CONT" "ARC_CONT"
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
"ARC Continue Specify end point [Ctrl = flip direction]:".into() t!("ARC Continue Specify end point [Ctrl = flip direction]:").into_owned()
} }
fn set_ctrl(&mut self, ctrl: bool) { fn set_ctrl(&mut self, ctrl: bool) {
self.ctrl = ctrl; self.ctrl = ctrl;

View file

@ -10,6 +10,7 @@ use acadrust::entities::AttributeDefinition;
use acadrust::types::Vector3; use acadrust::types::Vector3;
use acadrust::EntityType; use acadrust::EntityType;
use glam::DVec3; use glam::DVec3;
use crate::t;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
@ -52,12 +53,22 @@ impl CadCommand for AttdefCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
Step::Tag => "ATTDEF Enter attribute tag (no spaces):".into(), Step::Tag => t!("ATTDEF Enter attribute tag (no spaces):").into_owned(),
Step::Prompt { tag } => format!("ATTDEF Enter prompt for '{tag}' (Enter=use tag):"), Step::Prompt { tag } => t!(
Step::Default { tag, .. } => { "ATTDEF Enter prompt for '%{tag}' (Enter=use tag):",
format!("ATTDEF Enter default value for '{tag}' (Enter=blank):") tag = tag
} )
Step::Insertion { tag, .. } => format!("ATTDEF Specify insertion point for '{tag}':"), .into_owned(),
Step::Default { tag, .. } => t!(
"ATTDEF Enter default value for '%{tag}' (Enter=blank):",
tag = tag
)
.into_owned(),
Step::Insertion { tag, .. } => t!(
"ATTDEF Specify insertion point for '%{tag}':",
tag = tag
)
.into_owned(),
} }
} }

View file

@ -17,6 +17,7 @@
use acadrust::types::Vector3; use acadrust::types::Vector3;
use acadrust::{EntityType, Handle, Line}; use acadrust::{EntityType, Handle, Line};
use glam::DVec3; use glam::DVec3;
use crate::t;
use crate::command::{CadCommand, CmdResult}; use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::modules::{IconKind, ModuleEvent, ToolDef};
@ -216,9 +217,9 @@ impl CadCommand for CenterLineCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
if self.first.is_none() { if self.first.is_none() {
"CENTERLINE Select first line:".to_string() t!("CENTERLINE Select first line:").into_owned()
} else { } else {
"CENTERLINE Select second line:".to_string() t!("CENTERLINE Select second line:").into_owned()
} }
} }

View file

@ -10,6 +10,7 @@
use acadrust::types::Vector3; use acadrust::types::Vector3;
use acadrust::{Circle, EntityType}; use acadrust::{Circle, EntityType};
use crate::t;
use crate::command::{CadCommand, CmdResult, DynField, TangentObject}; use crate::command::{CadCommand, CmdResult, DynField, TangentObject};
use crate::modules::draw::defaults; use crate::modules::draw::defaults;
@ -135,11 +136,19 @@ impl CadCommand for CircleCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
StepCR::Center => "CIRCLE Specify center point:".into(), StepCR::Center => t!("CIRCLE Specify center point:").into_owned(),
StepCR::Radius(c) => format!( StepCR::Radius(c) => {
"CIRCLE Specify radius or type value <{:.4}> [center ({:.3},{:.3})]:", let r = format!("{:.4}", self.default_r);
self.default_r, c.x, c.y let cx = format!("{:.3}", c.x);
), let cy = format!("{:.3}", c.y);
t!(
"CIRCLE Specify radius or type value <%{r}> [center (%{cx},%{cy})]:",
r = r,
cx = cx,
cy = cy
)
.into_owned()
}
} }
} }
@ -151,7 +160,7 @@ impl CadCommand for CircleCommand {
CmdOption::new("2P", "2P"), CmdOption::new("2P", "2P"),
CmdOption::new("Ttr", "TTR"), CmdOption::new("Ttr", "TTR"),
CmdOption::new("Ttt", "TTT"), CmdOption::new("Ttt", "TTT"),
CmdOption::new("Diameter", "D"), CmdOption::new(t!("Diameter").as_ref(), "D"),
], ],
StepCR::Radius(_) => vec![], StepCR::Radius(_) => vec![],
} }
@ -259,11 +268,12 @@ impl CadCommand for CircleCDCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
StepCR::Center => "CIRCLE CD Specify center point:".into(), StepCR::Center => t!("CIRCLE CD Specify center point:").into_owned(),
StepCR::Radius(c) => format!( StepCR::Radius(c) => crate::tf!(
"CIRCLE CD Specify diameter or type value <{:.4}> [center ({:.3},{:.3})]:", "CIRCLE CD Specify diameter or type value <{:.4}> [center ({:.3},{:.3})]:",
self.default_d, c.x, c.y self.default_d, c.x, c.y
), )
.into_owned(),
} }
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, pt: DVec3) -> CmdResult {
@ -343,9 +353,9 @@ impl CadCommand for Circle2PCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
if self.p1.is_none() { if self.p1.is_none() {
"CIRCLE 2P Specify first end of diameter:".into() crate::t!("CIRCLE 2P Specify first end of diameter:").into_owned()
} else { } else {
"CIRCLE 2P Specify second end of diameter:".into() crate::t!("CIRCLE 2P Specify second end of diameter:").into_owned()
} }
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, pt: DVec3) -> CmdResult {
@ -393,9 +403,9 @@ impl CadCommand for Circle3PCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.pts.len() { match self.pts.len() {
0 => "CIRCLE 3P Specify first point:".into(), 0 => crate::t!("CIRCLE 3P Specify first point:").into_owned(),
1 => "CIRCLE 3P Specify second point:".into(), 1 => crate::t!("CIRCLE 3P Specify second point:").into_owned(),
_ => "CIRCLE 3P Specify third point:".into(), _ => crate::t!("CIRCLE 3P Specify third point:").into_owned(),
} }
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, pt: DVec3) -> CmdResult {
@ -897,9 +907,9 @@ impl CadCommand for CircleTTRCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match &self.step { match &self.step {
StepTTR::First => "CIRCLE TTR Select first tangent object:".into(), StepTTR::First => crate::t!("CIRCLE TTR Select first tangent object:").into_owned(),
StepTTR::Second { .. } => "CIRCLE TTR Select second tangent object:".into(), StepTTR::Second { .. } => crate::t!("CIRCLE TTR Select second tangent object:").into_owned(),
StepTTR::Radius { .. } => "CIRCLE TTR Specify radius:".into(), StepTTR::Radius { .. } => crate::t!("CIRCLE TTR Specify radius:").into_owned(),
} }
} }
@ -992,9 +1002,9 @@ impl CadCommand for CircleTTTCommand {
fn prompt(&self) -> String { fn prompt(&self) -> String {
match self.objs.len() { match self.objs.len() {
0 => "CIRCLE TTT Select first tangent object:".into(), 0 => crate::t!("CIRCLE TTT Select first tangent object:").into_owned(),
1 => "CIRCLE TTT Select second tangent object:".into(), 1 => crate::t!("CIRCLE TTT Select second tangent object:").into_owned(),
_ => "CIRCLE TTT Select third tangent object:".into(), _ => crate::t!("CIRCLE TTT Select third tangent object:").into_owned(),
} }
} }

Some files were not shown because too many files have changed in this diff Show more