mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-30 22:06:49 -06:00
* Save As suggests the default save format for opened drawings too (#2199) Options > Open and Save > "Save as" in AutoCAD is the format every drawing is saved in. CADCraft applied File > Default Save Format only to untitled drawings, so Save As on an opened DWG kept DWG with DXF chosen, and the other way round. Save As now swaps a DWG, DXF or DWT extension for the chosen format; other names, and Save of an opened file, are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Default Save Format's echo says Save As will suggest the format (#2199) "Save As will save drawings as DWG" promised more than Save As does: it only swaps a DWG, DXF or DWT extension in the name it suggests, and the path picked in the dialog decides the format. The echo now reads "Save As will suggest DWG", and default_save_format_applies_to_opened_drawings pins it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
43c41b8e68
commit
8d1bd19ee2
+24
-12
@@ -118,7 +118,7 @@ pub struct UiState {
|
||||
/// Animation effects (dialog and menu fades, smoothed scrolling): "system" (follow the OS
|
||||
/// setting), "on" or "off".
|
||||
pub animations: animation::AnimationPref,
|
||||
/// The format Save As suggests for a drawing that has no file yet: "dxf" or "dwg".
|
||||
/// The format Save As suggests: "dxf" or "dwg", for new drawings and opened files alike.
|
||||
pub save_format: SaveFormat,
|
||||
/// The version Save As writes for a drawing that has no file yet ("2000" … "2018"); a
|
||||
/// drawing opened from a file keeps that file's version.
|
||||
@@ -132,8 +132,8 @@ pub struct UiState {
|
||||
/// A DXF/DWG file version (2000 … 2018).
|
||||
pub use cadcraft_engine::cmd::file::Version as SaveVersion;
|
||||
|
||||
/// The default save format for new drawings, like AutoCAD's Options > Open and Save > "Save as".
|
||||
/// A drawing opened from a file keeps that file's format.
|
||||
/// The default save format, like AutoCAD's Options > Open and Save > "Save as": the format Save As
|
||||
/// suggests for every drawing, an opened DWG or DXF too. Save (QSAVE) of an opened file keeps its file.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum SaveFormat {
|
||||
@@ -158,13 +158,19 @@ impl SaveFormat {
|
||||
SaveFormat::ALL.into_iter().find(|f| f.as_str().eq_ignore_ascii_case(s.trim().trim_start_matches('.')))
|
||||
}
|
||||
|
||||
/// The name Save As suggests: a title that already has an extension (a drawing opened from a
|
||||
/// file) keeps it; an untitled drawing gets this format's extension.
|
||||
/// The name Save As suggests: this format's extension, replacing a drawing's own (DWG, DXF or
|
||||
/// DWT, from a drawing opened from a file); any other extension is kept.
|
||||
pub fn suggested_name(self, title: Option<&str>) -> String {
|
||||
match title.map(str::trim).filter(|t| !t.is_empty()) {
|
||||
Some(t) if std::path::Path::new(t).extension().is_some() => t.to_owned(),
|
||||
Some(t) => format!("{t}.{}", self.as_str()),
|
||||
None => format!("Drawing.{}", self.as_str()),
|
||||
let Some(t) = title.map(str::trim).filter(|t| !t.is_empty()) else { return format!("Drawing.{}", self.as_str()) };
|
||||
let path = std::path::Path::new(t);
|
||||
match path.extension().and_then(|e| e.to_str()) {
|
||||
Some(e) if ["dwg", "dxf", "dwt"].iter().any(|d| d.eq_ignore_ascii_case(e)) => {
|
||||
// the extension is ASCII, so this is a char boundary
|
||||
let stem = t.get(..t.len().saturating_sub(e.len() + 1)).unwrap_or(t);
|
||||
format!("{stem}.{}", self.as_str())
|
||||
}
|
||||
Some(_) => t.to_owned(),
|
||||
None => format!("{t}.{}", self.as_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1142,11 +1148,17 @@ mod tests {
|
||||
b.start("ui.saveformat.dwg");
|
||||
b.start("ui.resetpalettes");
|
||||
assert_eq!(b.ui.save_format, SaveFormat::Dwg);
|
||||
// Untitled drawings get the format's extension; files keep their own.
|
||||
// Untitled drawings get the format's extension, and so do opened drawings (#2199).
|
||||
assert_eq!(SaveFormat::Dwg.suggested_name(Some("Drawing1")), "Drawing1.dwg");
|
||||
assert_eq!(SaveFormat::Dxf.suggested_name(Some("Drawing1")), "Drawing1.dxf");
|
||||
assert_eq!(SaveFormat::Dwg.suggested_name(Some("Floorplan.dxf")), "Floorplan.dxf");
|
||||
assert_eq!(SaveFormat::Dxf.suggested_name(Some("Floorplan.DWG")), "Floorplan.DWG");
|
||||
assert_eq!(SaveFormat::Dwg.suggested_name(Some("Floorplan.dxf")), "Floorplan.dwg");
|
||||
assert_eq!(SaveFormat::Dxf.suggested_name(Some("Floorplan.DWG")), "Floorplan.dxf");
|
||||
assert_eq!(SaveFormat::Dxf.suggested_name(Some("Floorplan.dxf")), "Floorplan.dxf");
|
||||
assert_eq!(SaveFormat::Dwg.suggested_name(Some("Title block.dwt")), "Title block.dwg");
|
||||
assert_eq!(SaveFormat::Dwg.suggested_name(Some("v1.2 plan.Dxf")), "v1.2 plan.dwg");
|
||||
assert_eq!(SaveFormat::Dwg.suggested_name(Some("平面図.dxf")), "平面図.dwg");
|
||||
assert_eq!(SaveFormat::Dwg.suggested_name(Some(".dxf")), ".dxf.dwg", "a dot file has no extension");
|
||||
assert_eq!(SaveFormat::Dxf.suggested_name(Some("logo.svg")), "logo.svg", "other files keep their type");
|
||||
assert_eq!(SaveFormat::Dwg.suggested_name(None), "Drawing.dwg");
|
||||
assert_eq!(SaveFormat::Dwg.suggested_name(Some(" ")), "Drawing.dwg");
|
||||
let ui: UiState = serde_json::from_value(json!({ "saveFormat": "dwg" })).unwrap_or_default();
|
||||
|
||||
@@ -520,7 +520,7 @@ fn saveas_params(ui: &crate::UiState, session: &cadcraft_engine::Session, path:
|
||||
|
||||
fn set_save_format(app: &mut CadApp, format: crate::SaveFormat) -> Value {
|
||||
app.ui.save_format = format;
|
||||
app.session.echo(format!("New drawings will be saved as {}", format.as_str().to_ascii_uppercase()));
|
||||
app.session.echo(format!("Save As will suggest {}", format.as_str().to_ascii_uppercase()));
|
||||
json!({ "saveFormat": format.as_str() })
|
||||
}
|
||||
|
||||
|
||||
@@ -44,3 +44,37 @@ fn default_save_version_applies_to_new_drawings_and_persists() {
|
||||
b.start("ui.resetpalettes");
|
||||
assert_eq!(b.ui.save_version, "2004");
|
||||
}
|
||||
|
||||
/// The default save format (#2199) is what Save As suggests for an opened DWG or DXF too, as
|
||||
/// AutoCAD's Options > Open and Save > "Save as" does; Save keeps the opened file.
|
||||
#[test]
|
||||
fn default_save_format_applies_to_opened_drawings() {
|
||||
set_io(IoHooks { read: |_, _| Ok(Drawing::new_metric()), write: |_, _| Ok(b"plain".to_vec()), plot: None });
|
||||
set_write_as(with_options);
|
||||
let dir = std::env::temp_dir().join(format!("cadcraft-ui-save-format-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let suggested = std::sync::Arc::new(std::sync::Mutex::new(Vec::<String>::new()));
|
||||
let (seen, to) = (suggested.clone(), dir.clone());
|
||||
let pick = move |name: &str| {
|
||||
seen.lock().unwrap().push(name.to_string());
|
||||
Some(to.join(name).to_string_lossy().to_string())
|
||||
};
|
||||
let mut app = CadApp::new(Session::empty(), Services { pick_save: Some(Box::new(pick)), ..Services::default() });
|
||||
for (opened, format, expected) in
|
||||
[("Plan.dwg", "dxf", "Plan.dxf"), ("Plan.DXF", "dwg", "Plan.dwg"), ("Plan.dxf", "dxf", "Plan.dxf"), ("Sheet.dwt", "dwg", "Sheet.dwg")]
|
||||
{
|
||||
let file = dir.join(opened);
|
||||
std::fs::write(&file, b"0\nEOF\n").unwrap();
|
||||
app.run("open", json!({ "path": file.to_string_lossy() })).unwrap();
|
||||
app.run("ui.saveformat", json!({ "format": format })).unwrap();
|
||||
// the picker's path decides the format, so the echo promises only the suggestion
|
||||
let echo = format!("Save As will suggest {}", format.to_ascii_uppercase());
|
||||
assert_eq!(app.session.log.last(), Some(&echo), "{opened} with {format}");
|
||||
app.run("ui.saveas", Value::Null).unwrap();
|
||||
assert_eq!(suggested.lock().unwrap().last().map(String::as_str), Some(expected), "{opened} with {format}");
|
||||
let path = app.session.state().unwrap().path.clone().unwrap_or_default();
|
||||
assert!(path.ends_with(expected), "{opened}: saved as {path}");
|
||||
assert!(dir.join(expected).exists());
|
||||
}
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user