diff --git a/Cargo.lock b/Cargo.lock index 216f0c28ae..f51ec42d4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1594,6 +1594,7 @@ dependencies = [ "ambient_core", "ambient_ecs", "ambient_gpu", + "ambient_guest_bridge", "ambient_input", "ambient_model", "ambient_network", @@ -1621,6 +1622,7 @@ dependencies = [ "itertools", "log", "once_cell", + "open", "parking_lot", "paste", "physxx", diff --git a/crates/wasm/Cargo.toml b/crates/wasm/Cargo.toml index 40710fb85f..0a75a869e3 100644 --- a/crates/wasm/Cargo.toml +++ b/crates/wasm/Cargo.toml @@ -20,11 +20,14 @@ ambient_gpu = { path = "../gpu" } ambient_renderer = { path = "../renderer" } ambient_procedurals = { path = "../procedurals" } +ambient_guest_bridge = { path = "../../shared_crates/guest_bridge", default-features = false, version = "0.3.0-dev" } + ambient_project = { path = "../../shared_crates/project" } ambient_shared_types = { path = "../../shared_crates/shared_types", features = [ "native", ] } + anyhow = { workspace = true } async-trait = { workspace = true } byteorder = { workspace = true } @@ -39,6 +42,7 @@ indoc = { workspace = true } itertools = { workspace = true } log = { workspace = true } once_cell = { workspace = true } +open = { workspace = true } parking_lot = { workspace = true } paste = { workspace = true } ambient_profiling = { workspace = true } diff --git a/crates/wasm/src/client/mod.rs b/crates/wasm/src/client/mod.rs index cf6372fc85..25893e4c22 100644 --- a/crates/wasm/src/client/mod.rs +++ b/crates/wasm/src/client/mod.rs @@ -1,6 +1,7 @@ use crate::shared::{self, client_bytecode_from_url, module_bytecode, ModuleBytecode}; use ambient_core::{asset_cache, async_ecs::async_run, runtime}; use ambient_ecs::{query, EntityId, SystemGroup, World}; +use ambient_guest_bridge::components::text::hyperlink; use ambient_std::{ asset_cache::AsyncAssetKeyExt, asset_url::AbsAssetUrl, download_asset::BytesFromUrl, }; @@ -27,6 +28,18 @@ pub fn systems() -> SystemGroup { SystemGroup::new( "core/wasm/client", vec![ + query(hyperlink().changed()).to_system(move |q, world, qs, _| { + for (id, url) in q.collect_cloned(world, qs) { + #[cfg(not(target_os = "unknown"))] + match open::that(&url) { + Ok(()) => log::info!("Opened '{}'", &url), + Err(err) => { + log::error!("An error occurred when opening '{}': {}", &url, err) + } + } + world.despawn(id); + } + }), query(client_bytecode_from_url().changed()).to_system(move |q, world, qs, _| { for (id, url) in q.collect_cloned(world, qs) { let url = match AbsAssetUrl::from_str(&url) { diff --git a/guest/rust/examples/ui/text/src/client.rs b/guest/rust/examples/ui/text/src/client.rs index 4d668e6f0d..cbbe434614 100644 --- a/guest/rust/examples/ui/text/src/client.rs +++ b/guest/rust/examples/ui/text/src/client.rs @@ -14,6 +14,8 @@ fn App(_hooks: &mut Hooks) -> Element { Text::el("Custom size").with(font_size(), 40.), Text::el("Custom color").with(color(), vec4(1., 0., 0., 1.)), Text::el("Multi\n\nLine"), + Text::el("Hyperlink:"), + Hyperlink::el("https://ambient.run".to_string()), ]) .with_padding_even(STREET) .with(space_between_items(), 10.) diff --git a/shared_crates/schema/src/schema/text.toml b/shared_crates/schema/src/schema/text.toml index ae15bf91bc..90f8a08269 100644 --- a/shared_crates/schema/src/schema/text.toml +++ b/shared_crates/schema/src/schema/text.toml @@ -26,3 +26,14 @@ type = "String" name = "Text" description = "Create a text mesh on this entity." attributes = ["Debuggable", "Networked", "Store"] + + +[components."core::text::hyperlink"] +type = "String" +name = "Hyperlink" +description = """ +This is a hyperlink text. With this component, the entity is a special one. +Once spawned, the system will open the URL String in the browser. +Then the entity will be destroyed. +""" +attributes = ["Debuggable", "Networked", "Store"] diff --git a/shared_crates/ui/src/text.rs b/shared_crates/ui/src/text.rs index 4c055dddfe..e73a75d29b 100644 --- a/shared_crates/ui/src/text.rs +++ b/shared_crates/ui/src/text.rs @@ -2,12 +2,15 @@ use crate::{UIBase, UIElement}; use ambient_element::{element_component, Element, ElementComponentExt, Hooks}; -use ambient_guest_bridge::components::{ - app::{name, ui_scene}, - layout::{height, width}, - rendering::color, - text::{font_family, font_size, text}, - transform::mesh_to_local, +use ambient_guest_bridge::{ + components::{ + app::{name, ui_scene}, + layout::{height, width}, + rendering::color, + text::{font_family, font_size, hyperlink, text}, + transform::mesh_to_local, + }, + ecs::Entity, }; use glam::{vec4, Mat4}; @@ -68,3 +71,18 @@ pub fn FontAwesomeIcon( .to_string(), ) } + +#[element_component] +/// Hyperlink to a URL. This will open browser when clicked. +pub fn Hyperlink( + _hooks: &mut Hooks, + /// The URL to open + url: String, +) -> Element { + crate::prelude::Button::new(&url.clone(), move |world| { + let link = Entity::new().with(hyperlink(), url.clone()); + world.spawn(link); + }) + .style(crate::prelude::ButtonStyle::Inline) + .el() +} diff --git a/web/Cargo.lock b/web/Cargo.lock index bc733a4dc3..c84d0b2b5d 100644 --- a/web/Cargo.lock +++ b/web/Cargo.lock @@ -960,6 +960,7 @@ dependencies = [ "ambient_core", "ambient_ecs", "ambient_gpu", + "ambient_guest_bridge", "ambient_input", "ambient_model", "ambient_network", @@ -985,6 +986,7 @@ dependencies = [ "itertools", "log", "once_cell", + "open", "parking_lot", "paste", "pollster", @@ -4072,6 +4074,16 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "open" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2423ffbf445b82e58c3b1543655968923dd06f85432f10be2bb4f1b7122f98c" +dependencies = [ + "pathdiff", + "windows-sys 0.36.1", +] + [[package]] name = "openssl-probe" version = "0.1.5" @@ -4172,6 +4184,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -6186,6 +6204,19 @@ dependencies = [ "windows-targets 0.48.0", ] +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + [[package]] name = "windows-sys" version = "0.42.0" @@ -6261,6 +6292,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + [[package]] name = "windows_aarch64_msvc" version = "0.37.0" @@ -6279,6 +6316,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + [[package]] name = "windows_i686_gnu" version = "0.37.0" @@ -6297,6 +6340,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + [[package]] name = "windows_i686_msvc" version = "0.37.0" @@ -6315,6 +6364,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + [[package]] name = "windows_x86_64_gnu" version = "0.37.0" @@ -6345,6 +6400,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + [[package]] name = "windows_x86_64_msvc" version = "0.37.0"