forked from 1b-agent/opencadstudio-upstream
fix(annotate): complete table and data workflows
This commit is contained in:
1 parent
6505b27818
commit
a35c996f9f
17 files changed
+2481
-145
No files matched your search
@@ -22,6 +22,7 @@ pub fn tool() -> ToolDef {
|
||||
pub struct DataLinkPlaceCommand {
|
||||
table: Table,
|
||||
plane: WorkingPlane,
|
||||
command_name: &'static str,
|
||||
}
|
||||
|
||||
impl DataLinkPlaceCommand {
|
||||
@@ -30,6 +31,43 @@ impl DataLinkPlaceCommand {
|
||||
Self {
|
||||
table,
|
||||
plane: WorkingPlane::default(),
|
||||
command_name: "DATALINK",
|
||||
}
|
||||
}
|
||||
|
||||
/// Place a table that references a data-link object already stored in the
|
||||
/// drawing. Every populated cell is locked and points at the same link so
|
||||
/// update and save/reopen keep one stable relationship.
|
||||
pub fn existing(mut table: Table, link: acadrust::Handle) -> Self {
|
||||
use acadrust::entities::table::CellStateFlags;
|
||||
let rows = table.row_count() as i32;
|
||||
let columns = table.column_count() as i32;
|
||||
for row in &mut table.rows {
|
||||
for cell in &mut row.cells {
|
||||
cell.has_linked_data = true;
|
||||
cell.data_link_handle = Some(link);
|
||||
cell.data_link_rows = rows;
|
||||
cell.data_link_columns = columns;
|
||||
cell.state.insert(
|
||||
CellStateFlags::LINKED
|
||||
| CellStateFlags::CONTENT_LOCKED
|
||||
| CellStateFlags::FORMAT_LOCKED,
|
||||
);
|
||||
}
|
||||
}
|
||||
Self {
|
||||
table,
|
||||
plane: WorkingPlane::default(),
|
||||
command_name: "DATALINK",
|
||||
}
|
||||
}
|
||||
|
||||
/// Place a prepared, unlinked table (used by the extraction wizard).
|
||||
pub fn unlinked(table: Table) -> Self {
|
||||
Self {
|
||||
table,
|
||||
plane: WorkingPlane::default(),
|
||||
command_name: "DATAEXTRACTION",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,11 +143,15 @@ impl CadCommand for DataLinkPlaceCommand {
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"DATALINK"
|
||||
self.command_name
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
t!("DATALINK Specify insertion point:").into_owned()
|
||||
if self.command_name == "DATALINK" {
|
||||
t!("DATALINK Specify insertion point:").into_owned()
|
||||
} else {
|
||||
t!("DATAEXTRACTION Specify insertion point:").into_owned()
|
||||
}
|
||||
}
|
||||
|
||||
fn on_point(&mut self, point: DVec3) -> CmdResult {
|
||||
|
||||
@@ -35,6 +35,8 @@ pub fn tool() -> ToolDef {
|
||||
enum Step {
|
||||
Points,
|
||||
Format,
|
||||
Annotation,
|
||||
AnnotationOptions,
|
||||
}
|
||||
|
||||
pub struct LeaderCommand {
|
||||
@@ -50,6 +52,7 @@ pub struct LeaderCommand {
|
||||
gap: f64,
|
||||
arrow_size: f64,
|
||||
annotative: bool,
|
||||
annotation_lines: Vec<String>,
|
||||
}
|
||||
|
||||
impl LeaderCommand {
|
||||
@@ -76,10 +79,11 @@ impl LeaderCommand {
|
||||
gap: defaults.gap,
|
||||
arrow_size: defaults.arrow_size,
|
||||
annotative: defaults.annotative,
|
||||
annotation_lines: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(&self) -> CmdResult {
|
||||
fn finish(&self, annotation: Option<&str>, open_editor: bool) -> CmdResult {
|
||||
if self.verts.len() < 2 {
|
||||
return CmdResult::Cancel;
|
||||
}
|
||||
@@ -134,8 +138,13 @@ impl LeaderCommand {
|
||||
// when another representation becomes active.
|
||||
let mtext_height = displayed_height;
|
||||
|
||||
if annotation.is_none() {
|
||||
leader.creation_type = LeaderCreationType::NoAnnotation;
|
||||
return CmdResult::CommitAndExit(self.plane.place_entity(EntityType::Leader(leader)));
|
||||
}
|
||||
|
||||
let mtext = build_mtext(
|
||||
"",
|
||||
annotation.unwrap_or_default(),
|
||||
anchor,
|
||||
mtext_height,
|
||||
attach,
|
||||
@@ -150,6 +159,7 @@ impl LeaderCommand {
|
||||
self.plane.place_entity(EntityType::MText(mtext)),
|
||||
],
|
||||
edit_index: 1,
|
||||
open_editor,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,6 +178,16 @@ impl CadCommand for LeaderCommand {
|
||||
return t!("LEADER Enter leader formatting option [Spline/Straight/Arrow/None] <exit>:")
|
||||
.into_owned();
|
||||
}
|
||||
if self.step == Step::AnnotationOptions {
|
||||
return t!("LEADER Enter an annotation option [None/Mtext]:").into_owned();
|
||||
}
|
||||
if self.step == Step::Annotation {
|
||||
return if self.annotation_lines.is_empty() {
|
||||
t!("LEADER Enter first line of annotation text or <options>:").into_owned()
|
||||
} else {
|
||||
t!("LEADER Enter next line of annotation text:").into_owned()
|
||||
};
|
||||
}
|
||||
match self.verts.len() {
|
||||
0 => t!("LEADER Specify leader start point:").into_owned(),
|
||||
1 => t!("LEADER Specify next point:").into_owned(),
|
||||
@@ -185,6 +205,12 @@ impl CadCommand for LeaderCommand {
|
||||
CmdOption::new("None", "NONE"),
|
||||
];
|
||||
}
|
||||
if self.step == Step::AnnotationOptions {
|
||||
return vec![
|
||||
CmdOption::new("None", "NONE"),
|
||||
CmdOption::new("Mtext", "MTEXT"),
|
||||
];
|
||||
}
|
||||
if self.verts.len() >= 2 {
|
||||
vec![
|
||||
CmdOption::new("Annotation", "ANNOTATION"),
|
||||
@@ -197,8 +223,10 @@ impl CadCommand for LeaderCommand {
|
||||
}
|
||||
|
||||
fn input_kind(&self) -> InputKind {
|
||||
if self.step == Step::Format {
|
||||
if matches!(self.step, Step::Format | Step::AnnotationOptions) {
|
||||
InputKind::SingleToken
|
||||
} else if self.step == Step::Annotation {
|
||||
InputKind::FreeText
|
||||
} else {
|
||||
InputKind::Point
|
||||
}
|
||||
@@ -210,6 +238,17 @@ impl CadCommand for LeaderCommand {
|
||||
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
let keyword = text.trim().to_ascii_uppercase();
|
||||
if self.step == Step::Annotation {
|
||||
self.annotation_lines.push(text.to_string());
|
||||
return Some(CmdResult::NeedPoint);
|
||||
}
|
||||
if self.step == Step::AnnotationOptions {
|
||||
return match keyword.as_str() {
|
||||
"N" | "NONE" => Some(self.finish(None, false)),
|
||||
"M" | "MTEXT" => Some(self.finish(Some(""), true)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if self.step == Step::Format {
|
||||
match keyword.as_str() {
|
||||
"S" | "SPLINE" => self.path_type = LeaderPathType::Spline,
|
||||
@@ -225,7 +264,10 @@ impl CadCommand for LeaderCommand {
|
||||
return None;
|
||||
}
|
||||
match keyword.as_str() {
|
||||
"A" | "ANNOTATION" => Some(self.finish()),
|
||||
"A" | "ANNOTATION" => {
|
||||
self.step = Step::Annotation;
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
"F" | "FORMAT" => {
|
||||
self.step = Step::Format;
|
||||
Some(CmdResult::NeedPoint)
|
||||
@@ -250,8 +292,19 @@ impl CadCommand for LeaderCommand {
|
||||
if self.step == Step::Format {
|
||||
self.step = Step::Points;
|
||||
CmdResult::NeedPoint
|
||||
} else if self.step == Step::Annotation {
|
||||
if self.annotation_lines.is_empty() {
|
||||
self.step = Step::AnnotationOptions;
|
||||
CmdResult::NeedPoint
|
||||
} else {
|
||||
let text = self.annotation_lines.join("\n");
|
||||
self.finish(Some(&text), false)
|
||||
}
|
||||
} else if self.step == Step::AnnotationOptions {
|
||||
CmdResult::NeedPoint
|
||||
} else {
|
||||
self.finish()
|
||||
self.step = Step::Annotation;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// data rows. The command remembers its previous settings and supports either a
|
||||
// fixed insertion point or a two-corner sizing window.
|
||||
|
||||
use acadrust::entities::table::{CellStyle, CellStylePropertyFlags};
|
||||
use acadrust::entities::TableBuilder;
|
||||
use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
@@ -101,9 +102,47 @@ pub struct TableCommand {
|
||||
preview_scale: f64,
|
||||
title_row: bool,
|
||||
header_row: bool,
|
||||
row_style_overrides: Option<[CellStyle; 3]>,
|
||||
preview_enabled: bool,
|
||||
plane: WorkingPlane,
|
||||
}
|
||||
|
||||
fn cell_style_from_row(style: &acadrust::objects::RowCellStyle) -> CellStyle {
|
||||
let mut result = CellStyle::new();
|
||||
result.property_flags = CellStylePropertyFlags::DATA_TYPE
|
||||
| CellStylePropertyFlags::DATA_FORMAT
|
||||
| CellStylePropertyFlags::ALIGNMENT
|
||||
| CellStylePropertyFlags::CONTENT_COLOR
|
||||
| CellStylePropertyFlags::TEXT_STYLE
|
||||
| CellStylePropertyFlags::TEXT_HEIGHT
|
||||
| CellStylePropertyFlags::BACKGROUND_COLOR;
|
||||
result.content_color = style.text_color;
|
||||
result.text_style_handle = style.text_style_handle;
|
||||
result.text_style_name = style.text_style_name.clone();
|
||||
result.text_height = style.text_height;
|
||||
result.alignment = style.alignment as i32;
|
||||
result.background_color = style.fill_color;
|
||||
result.fill_enabled = style.fill_enabled;
|
||||
result.value_data_type = style.data_type;
|
||||
result.value_unit_type = style.unit_type;
|
||||
result.value_format = style.format_string.clone();
|
||||
result
|
||||
}
|
||||
|
||||
fn selected_row_style(
|
||||
table_style: &acadrust::objects::TableStyle,
|
||||
name: &str,
|
||||
) -> CellStyle {
|
||||
let style = if name.eq_ignore_ascii_case("Title") {
|
||||
&table_style.title_row_style
|
||||
} else if name.eq_ignore_ascii_case("Header") {
|
||||
&table_style.header_row_style
|
||||
} else {
|
||||
&table_style.data_row_style
|
||||
};
|
||||
cell_style_from_row(style)
|
||||
}
|
||||
|
||||
impl TableCommand {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -114,6 +153,8 @@ impl TableCommand {
|
||||
preview_scale: 1.0,
|
||||
title_row: true,
|
||||
header_row: true,
|
||||
row_style_overrides: None,
|
||||
preview_enabled: true,
|
||||
plane: WorkingPlane::default(),
|
||||
}
|
||||
}
|
||||
@@ -141,10 +182,60 @@ impl TableCommand {
|
||||
},
|
||||
title_row: !style.title_suppressed,
|
||||
header_row: !style.header_suppressed,
|
||||
row_style_overrides: None,
|
||||
preview_enabled: true,
|
||||
plane: WorkingPlane::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the insertion command from the values accepted by the Insert
|
||||
/// Table dialog. The dialog owns validation, so the command starts at the
|
||||
/// point/window placement step instead of asking for the same values again.
|
||||
pub fn configured(
|
||||
style: Option<(acadrust::Handle, &acadrust::objects::TableStyle)>,
|
||||
annotation_multiplier: f64,
|
||||
columns: usize,
|
||||
data_rows: usize,
|
||||
column_width: f64,
|
||||
row_height: f64,
|
||||
use_window: bool,
|
||||
preview_enabled: bool,
|
||||
row_style_names: [&str; 3],
|
||||
) -> Self {
|
||||
let mut command = style.map_or_else(Self::new, |(handle, style)| {
|
||||
Self::with_style(handle, style, annotation_multiplier)
|
||||
});
|
||||
let insertion_mode = if use_window {
|
||||
InsertionMode::Window
|
||||
} else {
|
||||
InsertionMode::Point
|
||||
};
|
||||
if let Ok(mut saved) = saved_defaults().lock() {
|
||||
*saved = TableDefaults {
|
||||
columns,
|
||||
data_rows,
|
||||
column_width,
|
||||
row_height,
|
||||
insertion_mode,
|
||||
};
|
||||
}
|
||||
command.step = if use_window {
|
||||
Step::WindowFirst { columns, data_rows }
|
||||
} else {
|
||||
Step::Insertion {
|
||||
columns,
|
||||
data_rows,
|
||||
column_width,
|
||||
row_height,
|
||||
}
|
||||
};
|
||||
command.preview_enabled = preview_enabled;
|
||||
command.row_style_overrides = style.map(|(_, table_style)| {
|
||||
row_style_names.map(|name| selected_row_style(table_style, name))
|
||||
});
|
||||
command
|
||||
}
|
||||
|
||||
fn defaults(&self) -> TableDefaults {
|
||||
let mut defaults = saved_defaults().lock().map(|value| *value).unwrap_or_default();
|
||||
if (defaults.column_width - DEFAULT_COL_WIDTH).abs() < 1.0e-9 {
|
||||
@@ -175,6 +266,11 @@ impl TableCommand {
|
||||
.column_width(column_width)
|
||||
.build();
|
||||
table.table_style_handle = self.style_handle;
|
||||
if let Some(styles) = &self.row_style_overrides {
|
||||
for (row_index, row) in table.rows.iter_mut().enumerate() {
|
||||
row.style = Some(styles[row_index.min(2)].clone());
|
||||
}
|
||||
}
|
||||
self.plane.place_entity(EntityType::Table(table))
|
||||
}
|
||||
|
||||
@@ -447,6 +543,9 @@ impl CadCommand for TableCommand {
|
||||
}
|
||||
|
||||
fn on_mouse_move(&mut self, point: DVec3) -> Option<WireModel> {
|
||||
if !self.preview_enabled {
|
||||
return None;
|
||||
}
|
||||
match self.step {
|
||||
Step::Insertion {
|
||||
columns,
|
||||
|
||||
Reference in new issue
Block a user