feat(options): set default save format
Avoid false data-loss warnings when preserving the source format.\n\nCloses #529
This commit is contained in:
parent
c21f3e1a19
commit
7f41e1666f
10 changed files with 223 additions and 53 deletions
|
|
@ -789,6 +789,8 @@ pub(super) struct OpenCADStudio {
|
||||||
save_dialog_filename: String,
|
save_dialog_filename: String,
|
||||||
/// True when triggered from the unsaved-changes flow.
|
/// True when triggered from the unsaved-changes flow.
|
||||||
save_dialog_for_unsaved: bool,
|
save_dialog_for_unsaved: bool,
|
||||||
|
/// User preference for the first save of a new/unsaved drawing.
|
||||||
|
default_save_format: String,
|
||||||
|
|
||||||
// ── DimStyle Dialog ───────────────────────────────────────────────────
|
// ── DimStyle Dialog ───────────────────────────────────────────────────
|
||||||
/// Name of the style currently shown in the dialog.
|
/// Name of the style currently shown in the dialog.
|
||||||
|
|
@ -1298,6 +1300,7 @@ pub enum ModalKind {
|
||||||
DimStyle,
|
DimStyle,
|
||||||
Unsaved,
|
Unsaved,
|
||||||
SaveDialog,
|
SaveDialog,
|
||||||
|
Options,
|
||||||
AecDropWarning,
|
AecDropWarning,
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
FileInUse,
|
FileInUse,
|
||||||
|
|
@ -1468,6 +1471,10 @@ pub enum Message {
|
||||||
SaveDialogCancel,
|
SaveDialogCancel,
|
||||||
/// Destination picked in the native OS save dialog (`None` = cancelled).
|
/// Destination picked in the native OS save dialog (`None` = cancelled).
|
||||||
SaveDialogPathPicked(Option<std::path::PathBuf>),
|
SaveDialogPathPicked(Option<std::path::PathBuf>),
|
||||||
|
/// Open the application-wide Options dialog.
|
||||||
|
OptionsOpen,
|
||||||
|
/// Set the default type/version used when first saving a new drawing.
|
||||||
|
DefaultSaveFormatChanged(String),
|
||||||
ClearScene,
|
ClearScene,
|
||||||
SetWireframe(bool),
|
SetWireframe(bool),
|
||||||
/// Set the active tab's render mode (one of acadrust's seven visual
|
/// Set the active tab's render mode (one of acadrust's seven visual
|
||||||
|
|
@ -2618,9 +2625,10 @@ impl OpenCADStudio {
|
||||||
pending_save_failure: None,
|
pending_save_failure: None,
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pending_external_change: None,
|
pending_external_change: None,
|
||||||
save_dialog_format: "DWG 2018".to_string(),
|
save_dialog_format: crate::io::DEFAULT_SAVE_FORMAT.to_string(),
|
||||||
save_dialog_filename: "drawing.dwg".to_string(),
|
save_dialog_filename: "drawing.dwg".to_string(),
|
||||||
save_dialog_for_unsaved: false,
|
save_dialog_for_unsaved: false,
|
||||||
|
default_save_format: crate::io::DEFAULT_SAVE_FORMAT.to_string(),
|
||||||
// Plot style
|
// Plot style
|
||||||
active_plot_style: None,
|
active_plot_style: None,
|
||||||
// Color scheme (default: dark CAD-style)
|
// Color scheme (default: dark CAD-style)
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,9 @@ pub struct UserSettings {
|
||||||
/// Minutes between autosaves to a `.sv$` recovery file (SAVETIME command).
|
/// Minutes between autosaves to a `.sv$` recovery file (SAVETIME command).
|
||||||
/// 0 disables autosave.
|
/// 0 disables autosave.
|
||||||
pub savetime_min: i32,
|
pub savetime_min: i32,
|
||||||
|
/// File type and version used when a new/unsaved drawing is first saved.
|
||||||
|
/// Existing drawings keep their own type and version.
|
||||||
|
pub default_save_format: String,
|
||||||
/// PICKADD (#226): `true` (default) = a plain click ADDS to the selection
|
/// PICKADD (#226): `true` (default) = a plain click ADDS to the selection
|
||||||
/// (Shift removes); `false` = OS-style — a click REPLACES the selection
|
/// (Shift removes); `false` = OS-style — a click REPLACES the selection
|
||||||
/// and Shift+click toggles membership.
|
/// and Shift+click toggles membership.
|
||||||
|
|
@ -169,6 +172,7 @@ impl Default for UserSettings {
|
||||||
backup_on_save: true,
|
backup_on_save: true,
|
||||||
file_assoc_enabled: true,
|
file_assoc_enabled: true,
|
||||||
savetime_min: 10,
|
savetime_min: 10,
|
||||||
|
default_save_format: crate::io::DEFAULT_SAVE_FORMAT.to_string(),
|
||||||
pick_add: true,
|
pick_add: true,
|
||||||
pick_drag_rect: false,
|
pick_drag_rect: false,
|
||||||
bg_color: None,
|
bg_color: None,
|
||||||
|
|
|
||||||
|
|
@ -26,18 +26,24 @@ impl OpenCADStudio {
|
||||||
// Default the format dropdown to the loaded file's own format — its
|
// Default the format dropdown to the loaded file's own format — its
|
||||||
// DWG-vs-DXF kind (from the extension) and its version (from the parsed
|
// DWG-vs-DXF kind (from the extension) and its version (from the parsed
|
||||||
// document) — so Save-As round-trips the format instead of silently
|
// document) — so Save-As round-trips the format instead of silently
|
||||||
// re-targeting the latest. A new/unsaved drawing has no path, so it
|
// re-targeting it. A new/unsaved drawing has no source format, so it
|
||||||
// keeps its document default version and DWG. The user can still pick
|
// uses the application-wide default chosen in Options (#529).
|
||||||
// any other version explicitly.
|
self.save_dialog_format = if let Some(path) = &self.tabs[tab_idx].current_path {
|
||||||
let is_dxf = self.tabs[tab_idx]
|
let is_dxf = path
|
||||||
.current_path
|
.extension()
|
||||||
.as_ref()
|
.and_then(|e| e.to_str())
|
||||||
.and_then(|p| p.extension())
|
.map(|e| e.eq_ignore_ascii_case("dxf"))
|
||||||
.and_then(|e| e.to_str())
|
.unwrap_or(false);
|
||||||
.map(|e| e.eq_ignore_ascii_case("dxf"))
|
let document = &self.tabs[tab_idx].scene.document;
|
||||||
.unwrap_or(false);
|
let version = if is_dxf {
|
||||||
let version = self.tabs[tab_idx].scene.document.version;
|
document.version
|
||||||
self.save_dialog_format = crate::io::format_for_version(version, is_dxf);
|
} else {
|
||||||
|
document.dwg_source_version.unwrap_or(document.version)
|
||||||
|
};
|
||||||
|
crate::io::format_for_version(version, is_dxf)
|
||||||
|
} else {
|
||||||
|
self.default_save_format.clone()
|
||||||
|
};
|
||||||
|
|
||||||
// Pre-fill the default file name from the current path or the tab name;
|
// Pre-fill the default file name from the current path or the tab name;
|
||||||
// the destination folder comes from the native OS dialog that follows.
|
// the destination folder comes from the native OS dialog that follows.
|
||||||
|
|
@ -280,7 +286,7 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
||||||
self.active_tab = idx;
|
self.active_tab = idx;
|
||||||
self.save_dialog_for_unsaved = true;
|
self.save_dialog_for_unsaved = true;
|
||||||
let close = self.close_unsaved_dialog_window();
|
let close = self.close_unsaved_dialog_window();
|
||||||
let save = self.save_default_dwg2018(idx);
|
let save = self.save_with_default_format(idx);
|
||||||
return Task::batch([close, save]);
|
return Task::batch([close, save]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -291,7 +297,7 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
||||||
self.pending_close = Some(crate::app::PendingClose::Tab(idx));
|
self.pending_close = Some(crate::app::PendingClose::Tab(idx));
|
||||||
self.save_dialog_for_unsaved = true;
|
self.save_dialog_for_unsaved = true;
|
||||||
let close = self.close_unsaved_dialog_window();
|
let close = self.close_unsaved_dialog_window();
|
||||||
let save = self.save_default_dwg2018(idx);
|
let save = self.save_with_default_format(idx);
|
||||||
Task::batch([close, save])
|
Task::batch([close, save])
|
||||||
}
|
}
|
||||||
Some(crate::app::PendingClose::Quit) => {
|
Some(crate::app::PendingClose::Quit) => {
|
||||||
|
|
@ -300,7 +306,7 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
||||||
self.pending_close = Some(crate::app::PendingClose::Quit);
|
self.pending_close = Some(crate::app::PendingClose::Quit);
|
||||||
self.save_dialog_for_unsaved = true;
|
self.save_dialog_for_unsaved = true;
|
||||||
let close = self.close_unsaved_dialog_window();
|
let close = self.close_unsaved_dialog_window();
|
||||||
let save = self.save_default_dwg2018(idx);
|
let save = self.save_with_default_format(idx);
|
||||||
Task::batch([close, save])
|
Task::batch([close, save])
|
||||||
} else {
|
} else {
|
||||||
Task::batch([
|
Task::batch([
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,7 @@ impl OpenCADStudio {
|
||||||
backup_on_save: self.backup_on_save,
|
backup_on_save: self.backup_on_save,
|
||||||
file_assoc_enabled: self.file_assoc_enabled,
|
file_assoc_enabled: self.file_assoc_enabled,
|
||||||
savetime_min: self.savetime_min,
|
savetime_min: self.savetime_min,
|
||||||
|
default_save_format: self.default_save_format.clone(),
|
||||||
pick_add: self.pick_add,
|
pick_add: self.pick_add,
|
||||||
pick_drag_rect: self.pick_drag_rect,
|
pick_drag_rect: self.pick_drag_rect,
|
||||||
bg_color: self.default_bg_color.map(f4_to_u3),
|
bg_color: self.default_bg_color.map(f4_to_u3),
|
||||||
|
|
@ -145,6 +146,8 @@ impl OpenCADStudio {
|
||||||
self.backup_on_save = s.backup_on_save;
|
self.backup_on_save = s.backup_on_save;
|
||||||
self.file_assoc_enabled = s.file_assoc_enabled;
|
self.file_assoc_enabled = s.file_assoc_enabled;
|
||||||
self.savetime_min = s.savetime_min;
|
self.savetime_min = s.savetime_min;
|
||||||
|
self.default_save_format =
|
||||||
|
crate::io::canonical_save_format(&s.default_save_format).to_string();
|
||||||
self.pick_add = s.pick_add;
|
self.pick_add = s.pick_add;
|
||||||
self.pick_drag_rect = s.pick_drag_rect;
|
self.pick_drag_rect = s.pick_drag_rect;
|
||||||
self.default_bg_color = s.bg_color.map(u3_to_f4);
|
self.default_bg_color = s.bg_color.map(u3_to_f4);
|
||||||
|
|
@ -1307,6 +1310,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
||||||
}
|
}
|
||||||
if outcome.set_current_path {
|
if outcome.set_current_path {
|
||||||
self.tabs[i].current_path = Some(outcome.path.clone());
|
self.tabs[i].current_path = Some(outcome.path.clone());
|
||||||
|
self.tabs[i].scene.document.version = outcome.version;
|
||||||
tasks.push(self.push_recent(outcome.path.clone()));
|
tasks.push(self.push_recent(outcome.path.clone()));
|
||||||
}
|
}
|
||||||
self.refresh_native_edit_guard_after_save(
|
self.refresh_native_edit_guard_after_save(
|
||||||
|
|
@ -1528,22 +1532,23 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.save_dialog_for_unsaved = false;
|
self.save_dialog_for_unsaved = false;
|
||||||
self.save_default_dwg2018(i)
|
self.save_with_default_format(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save without the version picker: default to DWG 2018 and go straight to
|
/// Save without the version picker: use the configured default and go straight to
|
||||||
/// the native destination dialog (native) or the browser download (web).
|
/// the native destination dialog (native) or the browser download (web).
|
||||||
/// Used by plain Save (QSAVE) on an as-yet-unsaved drawing and by the
|
/// Used by plain Save (QSAVE) on an as-yet-unsaved drawing and by the
|
||||||
/// save-before-close flow — the version picker is reserved for Save As.
|
/// save-before-close flow — the version picker is reserved for Save As.
|
||||||
pub(in crate::app) fn save_default_dwg2018(&mut self, tab_idx: usize) -> Task<Message> {
|
pub(in crate::app) fn save_with_default_format(&mut self, tab_idx: usize) -> Task<Message> {
|
||||||
self.active_tab = tab_idx;
|
self.active_tab = tab_idx;
|
||||||
self.save_dialog_format = "DWG 2018".to_string();
|
self.save_dialog_format = self.default_save_format.clone();
|
||||||
|
let (ext, _) = crate::io::parse_save_format(&self.save_dialog_format);
|
||||||
self.save_dialog_filename = self.tabs[tab_idx]
|
self.save_dialog_filename = self.tabs[tab_idx]
|
||||||
.current_path
|
.current_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|p| p.file_name())
|
.and_then(|p| p.file_name())
|
||||||
.map(|n| n.to_string_lossy().into_owned())
|
.map(|n| n.to_string_lossy().into_owned())
|
||||||
.unwrap_or_else(|| format!("{}.dwg", self.tabs[tab_idx].tab_display_name()));
|
.unwrap_or_else(|| format!("{}.{ext}", self.tabs[tab_idx].tab_display_name()));
|
||||||
self.aec_drop_acknowledged = false;
|
self.aec_drop_acknowledged = false;
|
||||||
self.on_save_dialog_confirm()
|
self.on_save_dialog_confirm()
|
||||||
}
|
}
|
||||||
|
|
@ -1625,6 +1630,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
||||||
match crate::io::save_to_bytes(&self.tabs[i].scene.document, ext, version) {
|
match crate::io::save_to_bytes(&self.tabs[i].scene.document, ext, version) {
|
||||||
Ok(bytes) => {
|
Ok(bytes) => {
|
||||||
crate::sys::download_bytes(&filename, &bytes);
|
crate::sys::download_bytes(&filename, &bytes);
|
||||||
|
self.tabs[i].scene.document.version = version;
|
||||||
self.tabs[i].dirty = false;
|
self.tabs[i].dirty = false;
|
||||||
self.command_line.push_output(&format!("Saved: {filename}"));
|
self.command_line.push_output(&format!("Saved: {filename}"));
|
||||||
true
|
true
|
||||||
|
|
@ -1700,12 +1706,24 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// AEC-drop warning → "Save in source version": switch the target to the
|
/// AEC-drop warning → "Save in source version": switch the target to the
|
||||||
/// document's own DWG version (where the unsupported objects round-trip as
|
/// document's source type and version, then save.
|
||||||
/// verbatim bytes), then save.
|
|
||||||
pub(super) fn on_aec_drop_same_version(&mut self) -> Task<Message> {
|
pub(super) fn on_aec_drop_same_version(&mut self) -> Task<Message> {
|
||||||
let src = self.tabs[self.active_tab].scene.document.version;
|
let tab = &self.tabs[self.active_tab];
|
||||||
self.save_dialog_format = crate::io::format_for_version(src, false);
|
let is_dxf = tab
|
||||||
// Strip any extension (e.g. .dxf) so the confirm path appends .dwg.
|
.current_path
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|path| path.extension())
|
||||||
|
.and_then(|extension| extension.to_str())
|
||||||
|
.map(|extension| extension.eq_ignore_ascii_case("dxf"))
|
||||||
|
.unwrap_or(false);
|
||||||
|
let document = &tab.scene.document;
|
||||||
|
let src = if is_dxf {
|
||||||
|
document.version
|
||||||
|
} else {
|
||||||
|
document.dwg_source_version.unwrap_or(document.version)
|
||||||
|
};
|
||||||
|
self.save_dialog_format = crate::io::format_for_version(src, is_dxf);
|
||||||
|
// Strip the old extension so the confirm path appends the source type.
|
||||||
let stem = std::path::Path::new(&self.save_dialog_filename)
|
let stem = std::path::Path::new(&self.save_dialog_filename)
|
||||||
.file_stem()
|
.file_stem()
|
||||||
.map(|s| s.to_string_lossy().into_owned())
|
.map(|s| s.to_string_lossy().into_owned())
|
||||||
|
|
|
||||||
|
|
@ -3663,7 +3663,18 @@ impl OpenCADStudio {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── About window ──────────────────────────────────────────────
|
// ── Options / About windows ───────────────────────────────────
|
||||||
|
Message::OptionsOpen => {
|
||||||
|
self.active_modal = Some(super::ModalKind::Options);
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::DefaultSaveFormatChanged(format) => {
|
||||||
|
self.default_save_format =
|
||||||
|
crate::io::canonical_save_format(&format).to_string();
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
Message::AboutOpen => {
|
Message::AboutOpen => {
|
||||||
self.active_modal = Some(super::ModalKind::About);
|
self.active_modal = Some(super::ModalKind::About);
|
||||||
Task::none()
|
Task::none()
|
||||||
|
|
|
||||||
|
|
@ -1708,6 +1708,7 @@ impl OpenCADStudio {
|
||||||
let (w, h) = match self.active_modal? {
|
let (w, h) = match self.active_modal? {
|
||||||
About => (440, 360),
|
About => (440, 360),
|
||||||
Shortcuts => (720, 520),
|
Shortcuts => (720, 520),
|
||||||
|
Options => (480, 190),
|
||||||
PluginManager => (520, 460),
|
PluginManager => (520, 460),
|
||||||
UpdateNotice => (560, 460),
|
UpdateNotice => (560, 460),
|
||||||
Layers => (900, 360),
|
Layers => (900, 360),
|
||||||
|
|
@ -2524,6 +2525,7 @@ pub(super) fn start_page_view<'a>(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
|
outline_btn("Options", Message::OptionsOpen).into(),
|
||||||
outline_btn("Plugins", Message::PluginManagerOpen).into(),
|
outline_btn("Plugins", Message::PluginManagerOpen).into(),
|
||||||
];
|
];
|
||||||
// The web build is already in the browser, so only the desktop offers a
|
// The web build is already in the browser, so only the desktop offers a
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ impl OpenCADStudio {
|
||||||
Some(K::About) => "About",
|
Some(K::About) => "About",
|
||||||
Some(K::Shortcuts) => "Keyboard Shortcuts",
|
Some(K::Shortcuts) => "Keyboard Shortcuts",
|
||||||
Some(K::Aliases) => "Command Aliases",
|
Some(K::Aliases) => "Command Aliases",
|
||||||
|
Some(K::Options) => "Options",
|
||||||
Some(K::PluginManager) => "Plugin Manager",
|
Some(K::PluginManager) => "Plugin Manager",
|
||||||
Some(K::UpdateNotice) => "Update Available",
|
Some(K::UpdateNotice) => "Update Available",
|
||||||
Some(K::Layers) => "Layer Manager",
|
Some(K::Layers) => "Layer Manager",
|
||||||
|
|
@ -61,6 +62,11 @@ impl OpenCADStudio {
|
||||||
super::super::ModalKind::Aliases => {
|
super::super::ModalKind::Aliases => {
|
||||||
sized(crate::ui::window::alias_editor::view_window(&self.alias_editor_rows), 480, 520)
|
sized(crate::ui::window::alias_editor::view_window(&self.alias_editor_rows), 480, 520)
|
||||||
}
|
}
|
||||||
|
super::super::ModalKind::Options => sized(
|
||||||
|
crate::ui::window::options::view_window(&self.default_save_format),
|
||||||
|
480,
|
||||||
|
190,
|
||||||
|
),
|
||||||
super::super::ModalKind::PluginManager => sized(
|
super::super::ModalKind::PluginManager => sized(
|
||||||
crate::ui::window::plugin_manager::view_window(
|
crate::ui::window::plugin_manager::view_window(
|
||||||
&self.disabled_plugins,
|
&self.disabled_plugins,
|
||||||
|
|
@ -574,7 +580,24 @@ impl OpenCADStudio {
|
||||||
let src_label = self
|
let src_label = self
|
||||||
.tabs
|
.tabs
|
||||||
.get(self.active_tab)
|
.get(self.active_tab)
|
||||||
.map(|t| crate::io::format_for_version(t.scene.document.version, false))
|
.map(|t| {
|
||||||
|
let is_dxf = t
|
||||||
|
.current_path
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|path| path.extension())
|
||||||
|
.and_then(|extension| extension.to_str())
|
||||||
|
.map(|extension| extension.eq_ignore_ascii_case("dxf"))
|
||||||
|
.unwrap_or(false);
|
||||||
|
let version = if is_dxf {
|
||||||
|
t.scene.document.version
|
||||||
|
} else {
|
||||||
|
t.scene
|
||||||
|
.document
|
||||||
|
.dwg_source_version
|
||||||
|
.unwrap_or(t.scene.document.version)
|
||||||
|
};
|
||||||
|
crate::io::format_for_version(version, is_dxf)
|
||||||
|
})
|
||||||
.unwrap_or_else(|| "DWG".to_string());
|
.unwrap_or_else(|| "DWG".to_string());
|
||||||
sized(
|
sized(
|
||||||
aec_drop_dialog_window(
|
aec_drop_dialog_window(
|
||||||
|
|
@ -689,11 +712,6 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SAVE_FORMAT_OPTIONS: &[&str] = &[
|
|
||||||
"DWG 2018", "DWG 2013", "DWG 2010", "DWG 2007", "DWG 2004", "DWG 2000", "DWG R14", "DXF 2018",
|
|
||||||
"DXF 2013", "DXF 2010", "DXF 2007", "DXF 2004", "DXF 2000", "DXF R14",
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Compact Save-As options dialog: pick the format/version and a default file
|
/// Compact Save-As options dialog: pick the format/version and a default file
|
||||||
/// name. The destination folder and overwrite confirmation come from the
|
/// name. The destination folder and overwrite confirmation come from the
|
||||||
/// native OS save dialog (native) or the browser download (web) that follows.
|
/// native OS save dialog (native) or the browser download (web) that follows.
|
||||||
|
|
@ -769,7 +787,10 @@ fn save_as_dialog_window<'a>(filename: &'a str, format: &'a str) -> Element<'a,
|
||||||
.padding([4, 12])
|
.padding([4, 12])
|
||||||
};
|
};
|
||||||
|
|
||||||
let sel_fmt = SAVE_FORMAT_OPTIONS.iter().copied().find(|&s| s == format);
|
let sel_fmt = crate::io::SAVE_FORMAT_OPTIONS
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.find(|&s| s == format);
|
||||||
let label = |s: &'static str| text(s).size(11).color(DIM);
|
let label = |s: &'static str| text(s).size(11).color(DIM);
|
||||||
|
|
||||||
let mut items: Vec<Element<'a, Message>> = Vec::new();
|
let mut items: Vec<Element<'a, Message>> = Vec::new();
|
||||||
|
|
@ -826,7 +847,7 @@ fn save_as_dialog_window<'a>(filename: &'a str, format: &'a str) -> Element<'a,
|
||||||
items.push(
|
items.push(
|
||||||
row![
|
row![
|
||||||
label("Format:").width(70),
|
label("Format:").width(70),
|
||||||
pick_list(SAVE_FORMAT_OPTIONS, sel_fmt, |s: &str| {
|
pick_list(crate::io::SAVE_FORMAT_OPTIONS, sel_fmt, |s: &str| {
|
||||||
Message::SaveDialogFormatChanged(s.to_string())
|
Message::SaveDialogFormatChanged(s.to_string())
|
||||||
})
|
})
|
||||||
.width(Fill),
|
.width(Fill),
|
||||||
|
|
|
||||||
|
|
@ -432,6 +432,21 @@ pub(crate) fn resolve_image_file(raw: &str, base_dir: Option<&Path>) -> Option<S
|
||||||
|
|
||||||
// ── Save ──────────────────────────────────────────────────────────────────
|
// ── Save ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
pub const DEFAULT_SAVE_FORMAT: &str = "DWG 2018";
|
||||||
|
|
||||||
|
pub const SAVE_FORMAT_OPTIONS: &[&str] = &[
|
||||||
|
"DWG 2018", "DWG 2013", "DWG 2010", "DWG 2007", "DWG 2004", "DWG 2000", "DWG R14", "DXF 2018",
|
||||||
|
"DXF 2013", "DXF 2010", "DXF 2007", "DXF 2004", "DXF 2000", "DXF R14",
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn canonical_save_format(format: &str) -> &'static str {
|
||||||
|
SAVE_FORMAT_OPTIONS
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.find(|candidate| candidate.eq_ignore_ascii_case(format))
|
||||||
|
.unwrap_or(DEFAULT_SAVE_FORMAT)
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a format string like "DWG 2013" or "DXF 2007" into
|
/// Parse a format string like "DWG 2013" or "DXF 2007" into
|
||||||
/// `(extension, DxfVersion)`. Falls back to ("dwg", AC1032) for unknown strings.
|
/// `(extension, DxfVersion)`. Falls back to ("dwg", AC1032) for unknown strings.
|
||||||
pub fn parse_save_format(format: &str) -> (&'static str, acadrust::DxfVersion) {
|
pub fn parse_save_format(format: &str) -> (&'static str, acadrust::DxfVersion) {
|
||||||
|
|
@ -486,6 +501,10 @@ pub fn dropped_on_save_count(
|
||||||
target_version: acadrust::DxfVersion,
|
target_version: acadrust::DxfVersion,
|
||||||
is_dxf: bool,
|
is_dxf: bool,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
if !is_dxf && doc.dwg_source_version == Some(target_version) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
let mut n = doc
|
let mut n = doc
|
||||||
.objects
|
.objects
|
||||||
.values()
|
.values()
|
||||||
|
|
@ -503,10 +522,6 @@ pub fn dropped_on_save_count(
|
||||||
|| raw_dwg_version.is_some_and(|source| source != target_version)
|
|| raw_dwg_version.is_some_and(|source| source != target_version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
acadrust::objects::ObjectType::GeoData(_)
|
|
||||||
| acadrust::objects::ObjectType::VisualStyle(_)
|
|
||||||
| acadrust::objects::ObjectType::Material(_)
|
|
||||||
| acadrust::objects::ObjectType::TableStyle(_) => !is_dxf,
|
|
||||||
_ => false,
|
_ => false,
|
||||||
})
|
})
|
||||||
.count();
|
.count();
|
||||||
|
|
@ -522,20 +537,6 @@ pub fn dropped_on_save_count(
|
||||||
.is_some_and(|source| source != target_version)
|
.is_some_and(|source| source != target_version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
acadrust::EntityType::Surface(entity) => {
|
|
||||||
is_dxf
|
|
||||||
|| entity.raw_dwg_data.is_none()
|
|
||||||
|| entity
|
|
||||||
.dwg_source_version
|
|
||||||
.is_some_and(|source| source != target_version)
|
|
||||||
}
|
|
||||||
acadrust::EntityType::Light(entity) => {
|
|
||||||
is_dxf
|
|
||||||
|| entity.raw_dwg_data.is_none()
|
|
||||||
|| entity
|
|
||||||
.dwg_source_version
|
|
||||||
.is_some_and(|source| source != target_version)
|
|
||||||
}
|
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
if dropped {
|
if dropped {
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,6 @@ pub mod shortcuts;
|
||||||
pub mod layers;
|
pub mod layers;
|
||||||
pub mod update_notice;
|
pub mod update_notice;
|
||||||
pub mod open_progress;
|
pub mod open_progress;
|
||||||
|
pub mod options;
|
||||||
pub mod attribute_editor;
|
pub mod attribute_editor;
|
||||||
pub mod alias_editor;
|
pub mod alias_editor;
|
||||||
|
|
|
||||||
98
src/ui/window/options.rs
Normal file
98
src/ui/window/options.rs
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
use crate::app::Message;
|
||||||
|
use iced::widget::{button, column, container, pick_list, row, text, Space};
|
||||||
|
use iced::{Background, Border, Color, Element, Fill, Theme};
|
||||||
|
|
||||||
|
pub fn view_window<'a>(default_save_format: &'a str) -> Element<'a, Message> {
|
||||||
|
const BG: Color = Color {
|
||||||
|
r: 0.15,
|
||||||
|
g: 0.15,
|
||||||
|
b: 0.17,
|
||||||
|
a: 1.0,
|
||||||
|
};
|
||||||
|
const BORDER: Color = Color {
|
||||||
|
r: 0.32,
|
||||||
|
g: 0.32,
|
||||||
|
b: 0.36,
|
||||||
|
a: 1.0,
|
||||||
|
};
|
||||||
|
const TEXT: Color = Color {
|
||||||
|
r: 0.90,
|
||||||
|
g: 0.90,
|
||||||
|
b: 0.90,
|
||||||
|
a: 1.0,
|
||||||
|
};
|
||||||
|
const DIM: Color = Color {
|
||||||
|
r: 0.60,
|
||||||
|
g: 0.60,
|
||||||
|
b: 0.64,
|
||||||
|
a: 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let selected = crate::io::SAVE_FORMAT_OPTIONS
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.find(|candidate| *candidate == default_save_format);
|
||||||
|
|
||||||
|
let close = button(text("Close").size(12).color(TEXT))
|
||||||
|
.on_press(Message::CloseModal)
|
||||||
|
.padding([5, 16])
|
||||||
|
.style(|_: &Theme, status| button::Style {
|
||||||
|
background: Some(Background::Color(match status {
|
||||||
|
button::Status::Hovered | button::Status::Pressed => Color {
|
||||||
|
r: 0.34,
|
||||||
|
g: 0.34,
|
||||||
|
b: 0.38,
|
||||||
|
a: 1.0,
|
||||||
|
},
|
||||||
|
_ => Color {
|
||||||
|
r: 0.26,
|
||||||
|
g: 0.26,
|
||||||
|
b: 0.29,
|
||||||
|
a: 1.0,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
text_color: TEXT,
|
||||||
|
border: Border {
|
||||||
|
color: BORDER,
|
||||||
|
width: 1.0,
|
||||||
|
radius: 4.0.into(),
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
let body = column![
|
||||||
|
text("Open and Save").size(15).color(TEXT),
|
||||||
|
Space::new().height(16),
|
||||||
|
row![
|
||||||
|
text("Default save format:").size(12).color(TEXT).width(150),
|
||||||
|
pick_list(crate::io::SAVE_FORMAT_OPTIONS, selected, |format: &str| {
|
||||||
|
Message::DefaultSaveFormatChanged(format.to_string())
|
||||||
|
})
|
||||||
|
.width(Fill),
|
||||||
|
]
|
||||||
|
.spacing(12)
|
||||||
|
.align_y(iced::Center),
|
||||||
|
Space::new().height(10),
|
||||||
|
text(
|
||||||
|
"Used when a new drawing is saved for the first time. Existing drawings keep their current file type and version."
|
||||||
|
)
|
||||||
|
.size(11)
|
||||||
|
.color(DIM)
|
||||||
|
.width(Fill),
|
||||||
|
Space::new().height(Fill),
|
||||||
|
row![Space::new().width(Fill), close],
|
||||||
|
]
|
||||||
|
.spacing(0)
|
||||||
|
.width(Fill)
|
||||||
|
.height(Fill);
|
||||||
|
|
||||||
|
container(body)
|
||||||
|
.style(|_: &Theme| container::Style {
|
||||||
|
background: Some(Background::Color(BG)),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.padding([16, 18])
|
||||||
|
.width(Fill)
|
||||||
|
.height(Fill)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue