forked from 1b-agent/opencadstudio-upstream
Merge remote-tracking branch 'origin/main' into codex/field-command
# Conflicts: # src/ui/window/attdef_dialog.rs
This commit is contained in:
commit
762cf1e7e4
68 files changed
+4665
-1048
No files matched your search
+66
-26
@@ -489,25 +489,50 @@ impl OpenCADStudio {
|
||||
self.active_modal = Some(crate::app::ModalKind::WriteBlock);
|
||||
}
|
||||
|
||||
// INSERT opens the Blocks palette on the drawing's blocks;
|
||||
// -INSERT runs on the command line.
|
||||
"INSERT" => {
|
||||
self.command_line.push_output("*Insert a block from the Blocks palette");
|
||||
self.open_blocks_palette(Some(crate::ui::window::block_palette::Tab::Current));
|
||||
}
|
||||
|
||||
"-INSERT" => {
|
||||
use crate::command::CadCommand;
|
||||
use crate::modules::insert::insert_block::InsertBlockCommand;
|
||||
let blocks = self.tabs[i].scene.custom_block_names();
|
||||
if blocks.is_empty() {
|
||||
self.command_line.push_error(
|
||||
crate::t!("No user-defined blocks found in this drawing.").as_ref(),
|
||||
);
|
||||
} else {
|
||||
use crate::modules::insert::insert_block::InsertBlockCommand;
|
||||
let ranked = self.ranked_block_names(&blocks);
|
||||
let snapshot = self.block_usage_snapshot();
|
||||
let cmd = InsertBlockCommand::new_with_usage(
|
||||
ranked,
|
||||
snapshot,
|
||||
self.cliprompt_lines.clamp(0, 50) as u8,
|
||||
);
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
let opts = cmd.options();
|
||||
self.command_line.set_step_options(opts.clone());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
let ranked = self.ranked_block_names(&blocks);
|
||||
let snapshot = self.block_usage_snapshot();
|
||||
let cmd = InsertBlockCommand::classic(
|
||||
ranked,
|
||||
snapshot,
|
||||
self.cliprompt_lines.clamp(0, 50) as u8,
|
||||
self.block_catalog(),
|
||||
self.insname.clone(),
|
||||
);
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
self.command_line.set_step_options(cmd.options());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
}
|
||||
|
||||
// A drawing named at -INSERT's block prompt: brought in as a
|
||||
// block, then placed.
|
||||
cmd if cmd.starts_with("_-INSERTFILE ") => {
|
||||
use crate::command::CadCommand;
|
||||
use crate::modules::insert::insert_block::InsertBlockCommand;
|
||||
let path = std::path::PathBuf::from(cmd["_-INSERTFILE ".len()..].trim().trim_matches('"'));
|
||||
match self.import_file_as_block(path) {
|
||||
Ok(name) => {
|
||||
let units = self
|
||||
.block_catalog()
|
||||
.units
|
||||
.remove(&name.to_ascii_uppercase())
|
||||
.unwrap_or_default();
|
||||
let preview = self.tabs[i].scene.block_preview_wires(&name);
|
||||
let cmd = InsertBlockCommand::classic_for_block(name, preview, units);
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
}
|
||||
Err(e) => self.command_line.push_error(e.as_str()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,15 +692,27 @@ impl OpenCADStudio {
|
||||
);
|
||||
}
|
||||
|
||||
// BLOCKPALETTE / BLOCKSPALETTE — toggle the docked Insert Block panel.
|
||||
"BLOCKPALETTE" | "BLOCKSPALETTE" => {
|
||||
self.show_block_palette ^= true;
|
||||
if self.show_block_palette {
|
||||
// Always open expanded so the panel is immediately usable;
|
||||
// the user can still collapse it via the pin (Auto) button.
|
||||
self.dock_expanded = Some(crate::ui::dock::PanelId::BlockPalette);
|
||||
self.refresh_block_palette();
|
||||
}
|
||||
// BLOCKSPALETTE opens the Blocks palette; BLOCKSPALETTECLOSE
|
||||
// closes it. `_BLOCKSPALETTE <tab>` opens it on a tab.
|
||||
"BLOCKPALETTE" | "BLOCKSPALETTE" => self.open_blocks_palette(None),
|
||||
"BLOCKSPALETTECLOSE" => {
|
||||
self.show_block_palette = false;
|
||||
self.block_palette.placing = None;
|
||||
}
|
||||
// A block picked in the ribbon gallery.
|
||||
cmd if cmd.starts_with("_BLOCKINSERT ") => {
|
||||
let name = cmd["_BLOCKINSERT ".len()..].trim().to_string();
|
||||
return Some(self.palette_insert(crate::ui::window::block_palette::Item::Current(name), None));
|
||||
}
|
||||
cmd if cmd.starts_with("_BLOCKSPALETTE ") => {
|
||||
use crate::ui::window::block_palette::Tab;
|
||||
let tab = match cmd["_BLOCKSPALETTE ".len()..].trim() {
|
||||
"RECENT" => Tab::Recent,
|
||||
"FAVORITES" => Tab::Favorites,
|
||||
"LIBRARIES" => Tab::Libraries,
|
||||
_ => Tab::Current,
|
||||
};
|
||||
self.open_blocks_palette(Some(tab));
|
||||
}
|
||||
|
||||
// ATTMAN / BATTMAN — the Block Attribute Manager. Rather than a
|
||||
@@ -1733,7 +1770,10 @@ mod tests {
|
||||
app.dock_expanded == Some(crate::ui::dock::PanelId::BlockPalette),
|
||||
"palette must open expanded, not as the collapsed bar"
|
||||
);
|
||||
// BLOCKSPALETTE only opens; BLOCKSPALETTECLOSE closes.
|
||||
let _ = app.run_command_line("BLOCKSPALETTE");
|
||||
assert!(app.show_block_palette);
|
||||
let _ = app.run_command_line("BLOCKSPALETTECLOSE");
|
||||
assert!(!app.show_block_palette);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
//! Block system variables. `BLOCKMRULIST`, `BLOCKREDEFINEMODE` and
|
||||
//! `BLOCKNAVIGATE` are application preferences kept with the settings;
|
||||
//! `EXPLMODE` lives in the drawing; `INSNAME` is the session's default block
|
||||
//! name; `BLOCKSTATE` is read-only. Each answers to its bare name (prompting
|
||||
//! for the value) and to `SETVAR NAME value`, with the reference's messages.
|
||||
|
||||
use crate::app::{Message, OpenCADStudio};
|
||||
use iced::Task;
|
||||
|
||||
/// The variables this family answers for, for the command registry and the
|
||||
/// `SETVAR ?` listing.
|
||||
pub(super) const BLOCK_SYSVARS: &[&str] = &[
|
||||
"BLOCKMRULIST",
|
||||
"BLOCKREDEFINEMODE",
|
||||
"BLOCKNAVIGATE",
|
||||
"BLOCKSTATE",
|
||||
"EXPLMODE",
|
||||
"INSNAME",
|
||||
];
|
||||
|
||||
/// The `SETVAR ?` line for this family.
|
||||
pub(super) fn setvar_listing() -> String {
|
||||
format!("SETVAR (block): {}", BLOCK_SYSVARS.join(" "))
|
||||
}
|
||||
|
||||
/// EXPLMODE of the drawing (default 1).
|
||||
pub(crate) fn explmode(document: &codec::CadDocument) -> i16 {
|
||||
crate::io::drawing_variable(document, "EXPLMODE")
|
||||
.and_then(|v| v.trim().parse().ok())
|
||||
.unwrap_or(1)
|
||||
}
|
||||
|
||||
impl OpenCADStudio {
|
||||
pub(super) fn dispatch_blockvars(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||
let rest = cmd.strip_prefix("SETVAR ").map(str::trim).unwrap_or(cmd);
|
||||
let mut parts = rest.splitn(2, char::is_whitespace);
|
||||
let name = parts.next().unwrap_or("").to_ascii_uppercase();
|
||||
if !BLOCK_SYSVARS.contains(&name.as_str()) {
|
||||
return None;
|
||||
}
|
||||
let value = parts.next().map(str::trim).filter(|v| !v.is_empty());
|
||||
match name.as_str() {
|
||||
"BLOCKSTATE" => {
|
||||
let state = self.tabs[i].refedit_session.is_some() as u8;
|
||||
self.command_line.push_output(&format!("BLOCKSTATE = {state} (read only)"));
|
||||
}
|
||||
"INSNAME" | "BLOCKNAVIGATE" => {
|
||||
let current = if name == "INSNAME" {
|
||||
self.insname.clone()
|
||||
} else {
|
||||
self.block_navigate.clone()
|
||||
};
|
||||
match value {
|
||||
Some(v) => {
|
||||
let v = if v == "." { String::new() } else { v.trim_matches('"').to_string() };
|
||||
if name == "INSNAME" {
|
||||
self.insname = v;
|
||||
} else {
|
||||
self.block_navigate = if v.is_empty() { ".".to_string() } else { v };
|
||||
self.persist_settings_if_changed();
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.command_line.push_output(&format!(
|
||||
"Enter new value for {name}, or . for none <\"{current}\">:"
|
||||
));
|
||||
self.pending_setvar = Some(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let (current, max) = match name.as_str() {
|
||||
"BLOCKMRULIST" => (self.block_mru_list as i32, 100),
|
||||
"BLOCKREDEFINEMODE" => (self.block_redefine_mode as i32, 2),
|
||||
_ => (explmode(&self.tabs[i].scene.document) as i32, 1),
|
||||
};
|
||||
let ask = |app: &mut Self, name: String| {
|
||||
app.command_line
|
||||
.push_output(&format!("Enter new value for {name} <{current}>:"));
|
||||
app.pending_setvar = Some(name);
|
||||
};
|
||||
match value {
|
||||
None => ask(self, name),
|
||||
Some(v) => match v.parse::<i32>() {
|
||||
Err(_) => {
|
||||
self.command_line.push_error("Requires an integer value.");
|
||||
ask(self, name);
|
||||
}
|
||||
Ok(n) if !(0..=max).contains(&n) => {
|
||||
self.command_line.push_error(&if max == 1 {
|
||||
"Requires 0 or 1 only.".to_string()
|
||||
} else {
|
||||
format!("Requires an integer between 0 and {max}.")
|
||||
});
|
||||
ask(self, name);
|
||||
}
|
||||
Ok(n) => match name.as_str() {
|
||||
"BLOCKMRULIST" => {
|
||||
self.block_mru_list = n as u8;
|
||||
self.trim_recent_blocks();
|
||||
self.persist_settings_if_changed();
|
||||
}
|
||||
"BLOCKREDEFINEMODE" => {
|
||||
self.block_redefine_mode = n as u8;
|
||||
self.persist_settings_if_changed();
|
||||
}
|
||||
_ => {
|
||||
if n != current {
|
||||
self.push_undo_snapshot(i, "EXPLMODE");
|
||||
crate::io::set_drawing_variable(
|
||||
&mut self.tabs[i].scene.document,
|
||||
"EXPLMODE",
|
||||
&n.to_string(),
|
||||
);
|
||||
self.tabs[i].dirty = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Task::none())
|
||||
}
|
||||
}
|
||||
@@ -1217,6 +1217,27 @@ impl OpenCADStudio {
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
} else {
|
||||
// EXPLMODE 0 keeps references that are not uniformly and
|
||||
// positively scaled whole.
|
||||
let selected: Vec<_> = if super::blockvars::explmode(&self.tabs[i].scene.document) == 0 {
|
||||
selected
|
||||
.into_iter()
|
||||
.filter(|(_, entity)| match entity {
|
||||
codec::EntityType::Insert(ins) => {
|
||||
let (x, y, z) = (ins.x_scale(), ins.y_scale(), ins.z_scale());
|
||||
let uniform = x > 0.0 && (x - y).abs() <= 1e-10 && (x - z).abs() <= 1e-10;
|
||||
if !uniform {
|
||||
self.command_line
|
||||
.push_output("The object is not uniformly and positively scaled.");
|
||||
}
|
||||
uniform
|
||||
}
|
||||
_ => true,
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
selected
|
||||
};
|
||||
let replacements = plan_explode(&selected, &self.tabs[i].scene.document);
|
||||
let exploded = replacements.len();
|
||||
if exploded > 0 {
|
||||
|
||||
+136
-22
@@ -342,33 +342,55 @@ impl OpenCADStudio {
|
||||
// line is fed through the same command path the `--script` startup
|
||||
// flag uses. Blank rows submit Enter to the active command.
|
||||
"SCRIPT" | "SCR" => {
|
||||
use crate::command::ValuePromptCommand;
|
||||
let c = ValuePromptCommand::new("SCRIPT", "SCRIPT path to the .scr file:");
|
||||
self.command_line.push_info(&c.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
return Some(Task::perform(
|
||||
async move {
|
||||
crate::sys::file_dialog()
|
||||
.add_filter("Script files (*.scr)", &["scr"])
|
||||
.pick_file()
|
||||
.await
|
||||
.map(|h| crate::sys::handle_path(&h))
|
||||
},
|
||||
|path| match path {
|
||||
Some(p) => Message::Command(format!("SCRIPT \"{}\"", p.display())),
|
||||
None => Message::Noop,
|
||||
},
|
||||
));
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
use crate::command::ValuePromptCommand;
|
||||
let c = ValuePromptCommand::new("SCRIPT", "SCRIPT path to the .scr file:");
|
||||
self.command_line.push_info(&c.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||
}
|
||||
}
|
||||
cmd if cmd.starts_with("SCRIPT ") || cmd.starts_with("SCR ") => {
|
||||
let path = cmd.split_once(' ').map(|(_, r)| r.trim().to_string());
|
||||
match path {
|
||||
Some(p) if !p.is_empty() => match std::fs::read_to_string(&p) {
|
||||
Ok(text) => {
|
||||
let cmds: Vec<Task<Message>> = text
|
||||
.lines()
|
||||
.map(str::trim)
|
||||
.filter(|l| !l.starts_with('#') && !l.starts_with(';'))
|
||||
.map(|l| Task::done(Message::ScriptLine(l.to_string())))
|
||||
.collect();
|
||||
self.command_line.push_output(crate::tf!(
|
||||
"SCRIPT: running {} command(s) from {p}.",
|
||||
cmds.len()
|
||||
).as_ref());
|
||||
return Some(Task::batch(cmds));
|
||||
Some(p) if !p.is_empty() => {
|
||||
let clean_path = p.trim().trim_matches(|c| c == '"' || c == '\'');
|
||||
match std::fs::read_to_string(clean_path) {
|
||||
Ok(text) => {
|
||||
let cmds: Vec<Task<Message>> = text
|
||||
.lines()
|
||||
.map(str::trim)
|
||||
.filter(|l| !l.starts_with('#') && !l.starts_with(';'))
|
||||
.map(|l| Task::done(Message::ScriptLine(l.to_string())))
|
||||
.collect();
|
||||
self.command_line.push_output(crate::tf!(
|
||||
"SCRIPT: running {} command(s) from {clean_path}.",
|
||||
cmds.len()
|
||||
).as_ref());
|
||||
return Some(Task::batch(cmds));
|
||||
}
|
||||
Err(e) => {
|
||||
self.command_line
|
||||
.push_error(crate::tf!("SCRIPT: cannot read {clean_path}: {e}").as_ref());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
self.command_line
|
||||
.push_error(crate::tf!("SCRIPT: cannot read {p}: {e}").as_ref());
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
self.command_line
|
||||
.push_info(crate::t!("Usage: SCRIPT <path to .scr file>").as_ref());
|
||||
@@ -421,3 +443,95 @@ fn parse_background_color(args: &[&str]) -> Option<[f32; 4]> {
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::app::{Message, OpenCADStudio};
|
||||
|
||||
fn fresh_app() -> OpenCADStudio {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
app.automation_op(r#"{"op":"new"}"#);
|
||||
app
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_script_draws_line() {
|
||||
let mut app = fresh_app();
|
||||
let i = app.active_tab;
|
||||
let before_count = app.tabs[i].scene.document.entities().count();
|
||||
|
||||
// Feed standard multi-line CAD script sequence
|
||||
let _ = app.update(Message::ScriptLine("LINE".into()));
|
||||
assert!(app.tabs[i].active_cmd.is_some(), "LINE command should be active");
|
||||
|
||||
let _ = app.update(Message::ScriptLine("0,0".into()));
|
||||
assert!(app.tabs[i].active_cmd.is_some(), "LINE command should stay active after first point");
|
||||
|
||||
let _ = app.update(Message::ScriptLine("10,10".into()));
|
||||
let _ = app.update(Message::ScriptLine("".into())); // Enter to finish
|
||||
|
||||
let after_count = app.tabs[i].scene.document.entities().count();
|
||||
assert_eq!(after_count, before_count + 1, "Line entity should have been created");
|
||||
assert!(app.tabs[i].active_cmd.is_none(), "LINE command should be finished");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_line_script_still_works() {
|
||||
let mut app = fresh_app();
|
||||
let i = app.active_tab;
|
||||
let before_count = app.tabs[i].scene.document.entities().count();
|
||||
|
||||
let _ = app.update(Message::ScriptLine("CIRCLE 5,5 2".into()));
|
||||
|
||||
let after_count = app.tabs[i].scene.document.entities().count();
|
||||
assert_eq!(after_count, before_count + 1, "Circle entity should have been created");
|
||||
assert!(app.tabs[i].active_cmd.is_none(), "CIRCLE command should be finished");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_name_prefixes_are_stripped() {
|
||||
let mut app = fresh_app();
|
||||
let i = app.active_tab;
|
||||
let before_count = app.tabs[i].scene.document.entities().count();
|
||||
|
||||
let _ = app.update(Message::ScriptLine("_CIRCLE 10,10 3".into()));
|
||||
|
||||
let after_count = app.tabs[i].scene.document.entities().count();
|
||||
assert_eq!(after_count, before_count + 1, "_CIRCLE should resolve to CIRCLE and create entity");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_script_scr_draws_all_entities() {
|
||||
let mut app = fresh_app();
|
||||
let i = app.active_tab;
|
||||
const SCRIPT: &str = r#"
|
||||
_LINE
|
||||
0,0
|
||||
100,0
|
||||
100,100
|
||||
0,100
|
||||
C
|
||||
|
||||
_LINE
|
||||
0,0
|
||||
100,100
|
||||
|
||||
_LINE
|
||||
0,100
|
||||
100,0
|
||||
|
||||
CIRCLE
|
||||
50,50
|
||||
35
|
||||
|
||||
CIRCLE 50,50 15
|
||||
|
||||
ZOOM EXTENTS
|
||||
"#;
|
||||
for line in SCRIPT.lines().map(str::trim).filter(|l| !l.starts_with('#') && !l.starts_with(';')) {
|
||||
let _ = app.update(Message::ScriptLine(line.to_string()));
|
||||
}
|
||||
// 4 square edges + 2 diagonal lines + 2 circles = 8 entities
|
||||
assert_eq!(app.tabs[i].scene.document.entities().count(), 8);
|
||||
}
|
||||
}
|
||||
+29
-1
@@ -22,6 +22,7 @@ mod pc_extract;
|
||||
mod pdf_underlay;
|
||||
mod pdf_dialogs;
|
||||
mod xclip;
|
||||
mod blockvars;
|
||||
mod plotvars;
|
||||
mod styleprops;
|
||||
mod view;
|
||||
@@ -198,7 +199,7 @@ impl OpenCADStudio {
|
||||
// editors (shortcuts, aliases) — none of them read the scene. This is
|
||||
// the single place that decides; `on_ribbon_tool_click` defers to it
|
||||
// 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(strip_command_prefixes(cmd).unwrap_or(cmd)) {
|
||||
self.command_line
|
||||
.push_info(crate::t!("No drawing open. Use NEW or OPEN to start a drawing.").as_ref());
|
||||
return Task::none();
|
||||
@@ -230,6 +231,15 @@ impl OpenCADStudio {
|
||||
return t;
|
||||
}
|
||||
|
||||
// Scripts and typed input may carry the international (`_`) and
|
||||
// built-in (`.`) name prefixes (`_LINE`, `._LINE`). Ribbon tools
|
||||
// dispatch internal names that start with `_` themselves
|
||||
// (`_PCCROPPOLY`), so a name is tried as written first and loses its
|
||||
// prefixes only when nothing handles it.
|
||||
if let Some(bare) = strip_command_prefixes(cmd) {
|
||||
return self.dispatch_command_inner(bare, allow_suggest);
|
||||
}
|
||||
|
||||
// No family matched. From the interactive command line, run the
|
||||
// closest autocomplete suggestion instead of erroring, so a partial
|
||||
// command completes on Enter (`BAC` → `BACKGROUND`). The verb's own
|
||||
@@ -304,6 +314,9 @@ impl OpenCADStudio {
|
||||
if let Some(t) = self.dispatch_plotvars(cmd, i) {
|
||||
return Some(t);
|
||||
}
|
||||
if let Some(t) = self.dispatch_blockvars(cmd, i) {
|
||||
return Some(t);
|
||||
}
|
||||
if let Some(t) = self.dispatch_styleprops(cmd, i) {
|
||||
return Some(t);
|
||||
}
|
||||
@@ -481,6 +494,14 @@ inventory::submit!(crate::command::CommandRegistration {
|
||||
// Block list + block-attribute list (command-line forms).
|
||||
"BLOCKPALETTE",
|
||||
"BLOCKSPALETTE",
|
||||
"BLOCKSPALETTECLOSE",
|
||||
"-INSERT",
|
||||
"BLOCKMRULIST",
|
||||
"BLOCKREDEFINEMODE",
|
||||
"BLOCKNAVIGATE",
|
||||
"BLOCKSTATE",
|
||||
"EXPLMODE",
|
||||
"INSNAME",
|
||||
"ATTMAN",
|
||||
"BATTMAN",
|
||||
// Drawing-content overview.
|
||||
@@ -1042,3 +1063,10 @@ mod marquee_cancel_tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// `cmd` without its leading `_` / `.` name prefixes (`_LINE`, `.LINE`,
|
||||
/// `._LINE`), or `None` when it carries none.
|
||||
fn strip_command_prefixes(cmd: &str) -> Option<&str> {
|
||||
let bare = cmd.trim_start_matches(['_', '.']);
|
||||
(bare.len() != cmd.len() && !bare.is_empty()).then_some(bare)
|
||||
}
|
||||
@@ -1107,6 +1107,7 @@ impl OpenCADStudio {
|
||||
let value = it.next().map(|s| s.trim().to_string());
|
||||
if name.is_empty() || name == "?" {
|
||||
self.command_line.push_info(&super::plotvars::setvar_listing());
|
||||
self.command_line.push_info(&super::blockvars::setvar_listing());
|
||||
self.command_line.push_info(
|
||||
crate::t!("SETVAR: CETRANSPARENCY LTSCALE CELTSCALE PDMODE PDSIZE TEXTSIZE ORTHOMODE FILLMODE MIRRTEXT FRAME IMAGEFRAME PDFFRAME WIPEOUTFRAME XCLIPFRAME POINTCLOUDCLIPFRAME ZOOMWHEEL ZOOMFACTOR SHORTCUTMENU SHORTCUTMENUDURATION CURSORSIZE PICKBOX CURSORTYPE SNAPANG TEXTFILL CLIPROMPTLINES COMMANDLINEFADETIME ATTREQ ATTDIA DIMASSOC DIMCONTINUEMODE CONSTRAINTSOLVEMODE CONSTRAINTINFER CONSTRAINTBARDISPLAY CONSTRAINTBARMODE CONSTRAINTNAMEFORMAT DYNCONSTRAINTDISPLAY ANGBASE ANGDIR SKETCHINC SKPOLY SKTOLERANCE DONUTID DONUTOD CENTEREXE CENTERLAYER CENTERLTYPE CENTERLTSCALE CENTERLTYPEFILE CENTERCROSSSIZE CENTERCROSSGAP CENTERMARKEXE COLORTHEME SELECTIONAREA SELECTIONAREAOPACITY SELECTIONEFFECT SELECTIONEFFECTCOLOR WINDOWSAREACOLOR CROSSINGAREACOLOR SELECTIONPREVIEW GRIPSIZE GRIPCOLOR GRIPHOT GRIPHOVER GRIPOBJLIMIT | CLAYER CELTYPE TEXTSTYLE (read-only)").as_ref(),
|
||||
);
|
||||
@@ -1688,11 +1689,11 @@ impl OpenCADStudio {
|
||||
},
|
||||
"ZOOMFACTOR" => match &value {
|
||||
Some(v) => match v.parse::<i32>() {
|
||||
Ok(factor) if (3..=100).contains(&factor) => {
|
||||
Ok(factor) if (3..=500).contains(&factor) => {
|
||||
self.zoom_factor = factor;
|
||||
Ok((format!("ZOOMFACTOR = {factor}"), true))
|
||||
}
|
||||
_ => Err("SETVAR: integer from 3 to 100 required.".into()),
|
||||
_ => Err("SETVAR: integer from 3 to 500 required.".into()),
|
||||
},
|
||||
None => {
|
||||
Ok((format!("ZOOMFACTOR = {}", self.zoom_factor), false))
|
||||
|
||||
Reference in new issue
Block a user