forked from 1b-agent/opencadstudio-upstream
fix(linux): focus browser on external links
This commit is contained in:
1 parent
dba8ec3d63
commit
9eb47c43a9
5 files changed
+135
-19
No files matched your search
Generated
+18
@@ -17,6 +17,7 @@ name = "OpenCADStudio"
|
||||
version = "0.9.0"
|
||||
dependencies = [
|
||||
"acadrust",
|
||||
"ashpd",
|
||||
"bincode",
|
||||
"bytemuck",
|
||||
"cavalier_contours",
|
||||
@@ -423,6 +424,23 @@ dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ashpd"
|
||||
version = "0.13.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb8421aaa9644a5faf26735f258b669b15f063313ef8f8e2bdb28912a1a6f111"
|
||||
dependencies = [
|
||||
"enumflags2",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"getrandom 0.4.3",
|
||||
"serde",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
version = "0.7.2"
|
||||
|
||||
@@ -101,6 +101,11 @@ naga = { version = "27", features = ["wgsl-in"] }
|
||||
# "Open with" for the portable .exe.
|
||||
windows-sys = { version = "0.61", features = ["Win32_UI_Shell", "Win32_UI_WindowsAndMessaging", "Win32_System_Com", "Win32_System_Registry", "Win32_Storage_FileSystem", "Win32_Foundation"] }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
# Mint an xdg-activation token for external URL handlers on Wayland. The token
|
||||
# is passed to xdg-open so an existing browser window may take focus.
|
||||
ashpd = { version = "0.13.13", default-features = false, features = ["async-io", "wayland"] }
|
||||
|
||||
[patch.crates-io]
|
||||
# Track standard DWG object relationships used by the scene integration.
|
||||
acadrust = { git = "https://github.com/OpenAEC-Foundation/acadifc.git", rev = "a7b1e07" }
|
||||
|
||||
@@ -4,19 +4,28 @@ impl OpenCADStudio {
|
||||
pub(super) fn dispatch_view(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||
match cmd {
|
||||
"DONATE" => {
|
||||
crate::sys::open_url("https://patreon.com/HakanSeven12");
|
||||
self.command_line.push_info("Opening Patreon page...");
|
||||
return Some(crate::sys::open_url(
|
||||
"https://patreon.com/HakanSeven12",
|
||||
self.main_window,
|
||||
));
|
||||
}
|
||||
|
||||
"WEBVERSION" => {
|
||||
crate::sys::open_url("https://hakanseven12.github.io/OpenCADStudio/");
|
||||
self.command_line.push_info("Opening OCS Web...");
|
||||
return Some(crate::sys::open_url(
|
||||
"https://hakanseven12.github.io/OpenCADStudio/",
|
||||
self.main_window,
|
||||
));
|
||||
}
|
||||
|
||||
"HELP" => {
|
||||
crate::sys::open_url("https://github.com/HakanSeven12/OpenCADStudio/discussions");
|
||||
self.command_line
|
||||
.push_info("Opening OCS Discussions for help and questions...");
|
||||
return Some(crate::sys::open_url(
|
||||
"https://github.com/HakanSeven12/OpenCADStudio/discussions",
|
||||
self.main_window,
|
||||
));
|
||||
}
|
||||
|
||||
// ── DWGPROPS — print round-trip-only HeaderVariables ─────────
|
||||
@@ -204,8 +213,8 @@ impl OpenCADStudio {
|
||||
"https://github.com/HakanSeven12/OpenCADStudio/issues/new?body={}",
|
||||
crate::sys::percent_encode(&body)
|
||||
);
|
||||
crate::sys::open_url(&url);
|
||||
self.command_line.push_info("Opening feedback page...");
|
||||
return Some(crate::sys::open_url(&url, self.main_window));
|
||||
}
|
||||
|
||||
"ABOUT" => {
|
||||
@@ -217,8 +226,11 @@ impl OpenCADStudio {
|
||||
}
|
||||
|
||||
"CHANGELOG" => {
|
||||
crate::sys::open_url("https://github.com/HakanSeven12/OpenCADStudio/releases");
|
||||
self.command_line.push_info("Opening release notes...");
|
||||
return Some(crate::sys::open_url(
|
||||
"https://github.com/HakanSeven12/OpenCADStudio/releases",
|
||||
self.main_window,
|
||||
));
|
||||
}
|
||||
|
||||
// ── ALIASEDIT — command-alias editor ───────────────────────────
|
||||
|
||||
@@ -290,10 +290,7 @@ impl OpenCADStudio {
|
||||
|
||||
Message::OpenPathPicked(None) => Task::none(),
|
||||
|
||||
Message::OpenUrl(url) => {
|
||||
crate::sys::open_url(&url);
|
||||
Task::none()
|
||||
}
|
||||
Message::OpenUrl(url) => crate::sys::open_url(&url, self.main_window),
|
||||
|
||||
Message::StartSectionSelect(section) => {
|
||||
self.start_section = section;
|
||||
@@ -5040,9 +5037,12 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
Message::UpdateNoticeOpenRelease => {
|
||||
crate::sys::open_url(crate::io::update_check::RELEASES_PAGE);
|
||||
let open = crate::sys::open_url(
|
||||
crate::io::update_check::RELEASES_PAGE,
|
||||
self.main_window,
|
||||
);
|
||||
self.close_active_modal();
|
||||
Task::none()
|
||||
open
|
||||
}
|
||||
Message::AssocPromptYes => {
|
||||
self.file_assoc_enabled = true;
|
||||
|
||||
+89
-8
@@ -1,20 +1,101 @@
|
||||
// Small platform shims for things the desktop build does natively but the web
|
||||
// (wasm) build must handle differently or skip.
|
||||
|
||||
/// Open a URL in the user's browser. The desktop launches the default handler;
|
||||
/// the web opens a new tab (the button click is a user gesture, so it isn't
|
||||
/// caught by the pop-up blocker). Focus of the opened page is left to the
|
||||
/// OS / browser.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn open_url(url: &str) {
|
||||
let _ = open::that(url);
|
||||
/// Open a URL in the user's browser.
|
||||
///
|
||||
/// Wayland requires an xdg-activation token from the source window before it
|
||||
/// will let an existing browser window take focus. Linux obtains that token
|
||||
/// from the OCS surface and passes it to xdg-open.
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn open_url<Message: Send + 'static>(
|
||||
url: &str,
|
||||
parent: Option<iced::window::Id>,
|
||||
) -> iced::Task<Message> {
|
||||
let url = url.to_string();
|
||||
match parent {
|
||||
Some(parent) => iced::window::run(parent, move |window| {
|
||||
open_url_linux(&url, linux_activation_token(window));
|
||||
})
|
||||
.discard(),
|
||||
None => {
|
||||
let _ = open::that_detached(url);
|
||||
iced::Task::none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn linux_activation_token(window: &dyn iced::window::Window) -> Option<String> {
|
||||
use iced::window::raw_window_handle::{RawDisplayHandle, RawWindowHandle};
|
||||
|
||||
let RawWindowHandle::Wayland(window_handle) =
|
||||
window.window_handle().ok()?.as_raw()
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
let RawDisplayHandle::Wayland(display_handle) =
|
||||
window.display_handle().ok()?.as_raw()
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// Keep the iced window borrowed while ashpd uses its raw Wayland surface.
|
||||
// The returned token is an owned string and is immediately handed to the
|
||||
// child process.
|
||||
unsafe {
|
||||
iced::futures::executor::block_on(
|
||||
ashpd::ActivationToken::from_wayland_raw(
|
||||
None,
|
||||
window_handle.surface.as_ptr(),
|
||||
display_handle.display.as_ptr(),
|
||||
),
|
||||
)
|
||||
}
|
||||
.map(String::from)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn open_url_linux(url: &str, activation_token: Option<String>) {
|
||||
let Some(token) = activation_token else {
|
||||
let _ = open::that_detached(url);
|
||||
return;
|
||||
};
|
||||
|
||||
let mut command = std::process::Command::new("xdg-open");
|
||||
command
|
||||
.arg(url)
|
||||
.env("XDG_ACTIVATION_TOKEN", &token)
|
||||
.env("DESKTOP_STARTUP_ID", token);
|
||||
|
||||
if let Ok(mut child) = command.spawn() {
|
||||
std::thread::spawn(move || {
|
||||
let _ = child.wait();
|
||||
});
|
||||
} else {
|
||||
let _ = open::that_detached(url);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "linux")))]
|
||||
pub fn open_url<Message>(
|
||||
url: &str,
|
||||
_parent: Option<iced::window::Id>,
|
||||
) -> iced::Task<Message> {
|
||||
let _ = open::that_detached(url);
|
||||
iced::Task::none()
|
||||
}
|
||||
|
||||
/// Web opens the tab synchronously so the browser still sees the click as a
|
||||
/// user gesture and does not block it as a pop-up.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn open_url(url: &str) {
|
||||
pub fn open_url<Message>(
|
||||
url: &str,
|
||||
_parent: Option<iced::window::Id>,
|
||||
) -> iced::Task<Message> {
|
||||
if let Some(window) = web_sys::window() {
|
||||
let _ = window.open_with_url_and_target(url, "_blank");
|
||||
}
|
||||
iced::Task::none()
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
|
||||
Reference in new issue
Block a user