forked from 1b-agent/opencadstudio-upstream
feat(app): first-launch prompt to set Open CAD Studio as default for .dwg/.dxf
On the first launch that hasn't answered it yet, the app shows a small
dialog offering to become the default handler for .dwg and .dxf files.
The choice is persisted (default_assoc_prompted) so the prompt never
reappears.
The platform plumbing lives in io/file_association.rs:
* Windows — opens the OS's own per-app default-programs dialog via
IApplicationAssociationRegistrationUI::LaunchAdvancedAssociationUI,
passing the "Open CAD Studio" RegisteredApplications name the MSI
registers. Modern Windows guards the actual default behind a
UserChoice hash, so this is the supported consent path.
* Linux — xdg-mime default against the installed .desktop file, whose
MimeType entries already cover image/vnd.dwg and image/vnd.dxf.
* macOS — LSSetDefaultRoleHandlerForContentType binds the DWG/DXF UTIs
declared in Info.plist to the app bundle id.
The work runs on a dedicated thread (the Windows dialog is modal) with
the result delivered back through a oneshot channel and surfaced in the
command line.
https://claude.ai/code/session_01DFVSKgViFaxEjZD2KsTdtt
This commit is contained in:
7 files changed
+457
-3
No files matched your search
@@ -85,6 +85,7 @@ impl OpenCADStudio {
|
||||
snap_enabled: self.snapper.snap_enabled,
|
||||
otrack: self.snapper.otrack_enabled,
|
||||
snap_modes: super::settings::UserSettings::modes_from(self.snapper.enabled.iter()),
|
||||
default_assoc_prompted: self.default_assoc_prompted,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +99,7 @@ impl OpenCADStudio {
|
||||
self.snapper.snap_enabled = s.snap_enabled;
|
||||
self.snapper.otrack_enabled = s.otrack;
|
||||
self.snapper.enabled = s.snap_modes.iter().copied().collect();
|
||||
self.default_assoc_prompted = s.default_assoc_prompted;
|
||||
}
|
||||
|
||||
/// Write preferences to disk only when they differ from the last write,
|
||||
@@ -110,6 +112,13 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
|
||||
/// Record that the one-time default-association prompt has been answered and
|
||||
/// flush it to disk, so the dialog never reappears on later launches.
|
||||
fn mark_assoc_prompted(&mut self) {
|
||||
self.default_assoc_prompted = true;
|
||||
self.persist_settings_if_changed();
|
||||
}
|
||||
|
||||
fn update_inner(&mut self, msg: Message) -> Task<Message> {
|
||||
match msg {
|
||||
Message::Tick(t) => {
|
||||
@@ -1613,6 +1622,12 @@ impl OpenCADStudio {
|
||||
if self.update_notice_window == Some(id) {
|
||||
self.update_notice_window = None;
|
||||
}
|
||||
// Closing the default-association prompt via the window chrome
|
||||
// counts as answering it — never nag again.
|
||||
if self.assoc_prompt_window == Some(id) {
|
||||
self.assoc_prompt_window = None;
|
||||
self.mark_assoc_prompted();
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
@@ -5647,6 +5662,36 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
Message::AssocPromptYes => {
|
||||
self.mark_assoc_prompted();
|
||||
let close = if let Some(id) = self.assoc_prompt_window.take() {
|
||||
window::close(id)
|
||||
} else {
|
||||
Task::none()
|
||||
};
|
||||
let run = Task::perform(
|
||||
crate::io::file_association::set_default_app(),
|
||||
Message::AssocResult,
|
||||
);
|
||||
Task::batch([close, run])
|
||||
}
|
||||
Message::AssocPromptNo => {
|
||||
self.mark_assoc_prompted();
|
||||
if let Some(id) = self.assoc_prompt_window.take() {
|
||||
window::close(id)
|
||||
} else {
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
Message::AssocResult(result) => {
|
||||
match result {
|
||||
Ok(msg) => self.command_line.push_info(&msg),
|
||||
Err(err) => self
|
||||
.command_line
|
||||
.push_error(&format!("Could not set default app: {err}")),
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::PageSetupWidthEdit(s) => {
|
||||
self.page_setup_w = s;
|
||||
Task::none()
|
||||
|
||||
Reference in new issue
Block a user