Skip to content

Commit

Permalink
Simplified API by removing the redundant selector-based canvas resolv…
Browse files Browse the repository at this point in the history
…er in the web target.
  • Loading branch information
anlumo committed Oct 3, 2022
1 parent eb5529e commit d491071
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 48 deletions.
2 changes: 1 addition & 1 deletion crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ impl Plugin for RenderPlugin {
let windows = app.world.resource_mut::<bevy_window::Windows>();
let raw_handle = windows.get_primary().map(|window| unsafe {
#[cfg(target_arch = "wasm32")]
if let Some(canvas) = &window.canvas_element {
if let Some(canvas) = window.canvas() {
return match canvas {
bevy_window::Canvas::HtmlCanvas(canvas) => {
instance.create_surface_from_canvas(canvas)
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_render/src/view/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ fn extract_windows(
size_changed: false,
present_mode_changed: false,
#[cfg(target_arch = "wasm32")]
canvas: window.canvas_element.clone(),
canvas: window.canvas().cloned(),
});

// NOTE: Drop the swap chain frame here
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_window/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ serde = { version = "1.0", features = ["derive"], optional = true }

[target.'cfg(target_arch = "wasm32")'.dependencies]
web-sys = "0.3"
wasm-bindgen = "0.2"
77 changes: 46 additions & 31 deletions crates/bevy_window/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use bevy_math::{DVec2, IVec2, UVec2, Vec2};
use bevy_reflect::{FromReflect, Reflect};
use bevy_utils::{tracing::warn, Uuid};
use raw_window_handle::RawWindowHandle;
use wasm_bindgen::JsCast;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Reflect, FromReflect)]
#[reflect_value(PartialEq, Hash)]
Expand Down Expand Up @@ -209,9 +210,9 @@ pub struct Window {
raw_window_handle: RawWindowHandleWrapper,
focused: bool,
mode: WindowMode,
canvas: Option<String>,
#[cfg(target_arch = "wasm32")]
pub canvas_element: Option<Canvas>,
canvas: Option<Canvas>,
#[cfg(target_arch = "wasm32")]
fit_canvas_to_parent: bool,
command_queue: Vec<WindowCommand>,
}
Expand Down Expand Up @@ -340,9 +341,9 @@ impl Window {
raw_window_handle: RawWindowHandleWrapper::new(raw_window_handle),
focused: true,
mode: window_descriptor.mode,
#[cfg(target_arch = "wasm32")]
canvas: window_descriptor.canvas.clone(),
#[cfg(target_arch = "wasm32")]
canvas_element: window_descriptor.canvas_element.clone(),
fit_canvas_to_parent: window_descriptor.fit_canvas_to_parent,
command_queue: Vec::new(),
}
Expand Down Expand Up @@ -698,12 +699,12 @@ impl Window {
});
}
/// Close the operating system window corresponding to this [`Window`].
///
///
/// This will also lead to this [`Window`] being removed from the
/// [`Windows`] resource.
///
/// If the default [`WindowPlugin`] is used, when no windows are
/// open, the [app will exit](bevy_app::AppExit).
/// open, the [app will exit](bevy_app::AppExit).
/// To disable this behaviour, set `exit_on_all_closed` on the [`WindowPlugin`]
/// to `false`
///
Expand All @@ -728,25 +729,21 @@ impl Window {
self.raw_window_handle.clone()
}

/// The "html canvas" element selector.
/// The canvas element.
///
/// If set, this selector will be used to find a matching html canvas element,
/// rather than creating a new one.
/// Uses the [CSS selector format](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector).
///
/// This value has no effect on non-web platforms.
/// If set, this canvas element will be used, rather than creating a new one.
#[cfg(target_arch = "wasm32")]
#[inline]
pub fn canvas(&self) -> Option<&str> {
self.canvas.as_deref()
pub fn canvas(&self) -> Option<&Canvas> {
self.canvas.as_ref()
}

/// Whether or not to fit the canvas element's size to its parent element's size.
///
/// **Warning**: this will not behave as expected for parents that set their size according to the size of their
/// children. This creates a "feedback loop" that will result in the canvas growing on each resize. When using this
/// feature, ensure the parent's size is not affected by its children.
///
/// This value has no effect on non-web platforms.
#[cfg(target_arch = "wasm32")]
#[inline]
pub fn fit_canvas_to_parent(&self) -> bool {
self.fit_canvas_to_parent
Expand Down Expand Up @@ -788,7 +785,9 @@ pub enum MonitorSelection {
#[cfg(target_arch = "wasm32")]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Canvas {
/// A canvas referenced by a regular HTML element. Does not have to be mounted to the DOM.
HtmlCanvas(web_sys::HtmlCanvasElement),
/// An OffscreenCanvas element, for example as produced by `HtmlCanvasElement::transfer_control_to_offscreen()`.
OffscreenCanvas(web_sys::OffscreenCanvas),
}
#[cfg(target_arch = "wasm32")]
Expand Down Expand Up @@ -866,31 +865,20 @@ pub struct WindowDescriptor {
/// macOS X transparent works with winit out of the box, so this issue might be related to: <https://github.com/gfx-rs/wgpu/issues/687>
/// Windows 11 is related to <https://github.com/rust-windowing/winit/issues/2082>
pub transparent: bool,
/// The "html canvas" element selector.
/// The canvas element.
///
/// If set, this selector will be used to find a matching html canvas element,
/// rather than creating a new one.
/// Uses the [CSS selector format](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector).
///
/// This value has no effect on non-web platforms.
pub canvas: Option<String>,
/// If set, this canvas element will be used, rather than creating a new one.
#[cfg(target_arch = "wasm32")]
pub canvas_element: Option<Canvas>,
pub canvas: Option<Canvas>,
/// Whether or not to fit the canvas element's size to its parent element's size.
///
/// **Warning**: this will not behave as expected for parents that set their size according to the size of their
/// children. This creates a "feedback loop" that will result in the canvas growing on each resize. When using this
/// feature, ensure the parent's size is not affected by its children.
///
/// This value has no effect on non-web platforms.
#[cfg(target_arch = "wasm32")]
pub fit_canvas_to_parent: bool,
}

#[cfg(target_arch = "wasm32")]
unsafe impl Send for WindowDescriptor {}
#[cfg(target_arch = "wasm32")]
unsafe impl Sync for WindowDescriptor {}

impl Default for WindowDescriptor {
fn default() -> Self {
WindowDescriptor {
Expand All @@ -908,10 +896,37 @@ impl Default for WindowDescriptor {
cursor_visible: true,
mode: WindowMode::Windowed,
transparent: false,
#[cfg(target_arch = "wasm32")]
canvas: None,
#[cfg(target_arch = "wasm32")]
canvas_element: None,
fit_canvas_to_parent: false,
}
}
}

#[cfg(target_arch = "wasm32")]
impl WindowDescriptor {
/// Convenience function for setting the canvas to be used by this WindowDescriptor.
///
/// The selector format used is a [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors).
/// It uses the first element matching the selector.
///
/// Returns an `Err` if the selector format is invalid. Panics if it is run from a web worker.
///
/// Returns `Ok(true)` if the canvas was found and `Ok(false)` otherwise. Note that matching a non-canvas element
/// also returns `Ok(false)`.
pub fn set_canvas_from_selector(
&mut self,
selector: &str,
) -> Result<bool, wasm_bindgen::JsValue> {
let canvas = web_sys::window()
.unwrap()
.document()
.unwrap()
.query_selector(selector)?;
self.canvas = canvas
.and_then(|canvas| canvas.dyn_into().ok())
.map(Canvas::HtmlCanvas);
Ok(self.canvas.is_some())
}
}
17 changes: 13 additions & 4 deletions crates/bevy_winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,12 +717,21 @@ fn handle_create_window_events(
{
let channel = world.resource_mut::<web_resize::CanvasParentResizeEventChannel>();
if create_window_event.descriptor.fit_canvas_to_parent {
let selector = if let Some(selector) = &create_window_event.descriptor.canvas {
selector
if let Some(canvas) = &create_window_event.descriptor.canvas {
match canvas {
bevy_window::Canvas::HtmlCanvas(canvas) => {
channel.listen_to_element(create_window_event.id, canvas.clone());
}
bevy_window::Canvas::OffscreenCanvas(_) => {
panic!("OffscreenCanvas not supported by winit!");
}
}
} else {
web_resize::WINIT_CANVAS_SELECTOR
channel.listen_to_selector(
create_window_event.id,
web_resize::WINIT_CANVAS_SELECTOR,
);
};
channel.listen_to_selector(create_window_event.id, selector);
}
}
}
Expand Down
32 changes: 32 additions & 0 deletions crates/bevy_winit/src/web_resize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use bevy_ecs::prelude::*;
use bevy_window::WindowId;
use crossbeam_channel::{Receiver, Sender};
use wasm_bindgen::JsCast;
use web_sys::HtmlCanvasElement;
use winit::dpi::LogicalSize;

pub(crate) struct CanvasParentResizePlugin;
Expand Down Expand Up @@ -49,6 +50,15 @@ fn get_size(selector: &str) -> Option<LogicalSize<f32>> {
));
}

fn get_size_element(element: &HtmlCanvasElement) -> Option<LogicalSize<f32>> {
let parent_element = element.parent_element()?;
let rect = parent_element.get_bounding_client_rect();
return Some(winit::dpi::LogicalSize::new(
rect.width() as f32,
rect.height() as f32,
));
}

pub(crate) const WINIT_CANVAS_SELECTOR: &str = "canvas[data-raw-handle]";

impl Default for CanvasParentResizeEventChannel {
Expand Down Expand Up @@ -81,4 +91,26 @@ impl CanvasParentResizeEventChannel {
.unwrap();
closure.forget();
}

pub(crate) fn listen_to_element(&self, window_id: WindowId, element: HtmlCanvasElement) {
let sender = self.sender.clone();
let resize = move || {
if let Some(size) = get_size_element(&element) {
sender.send(ResizeEvent { size, window_id }).unwrap();
}
};

// ensure resize happens on startup
resize();

let closure = wasm_bindgen::closure::Closure::wrap(Box::new(move |_: web_sys::Event| {
resize();
}) as Box<dyn FnMut(_)>);
let window = web_sys::window().unwrap();

window
.add_event_listener_with_callback("resize", closure.as_ref().unchecked_ref())
.unwrap();
closure.forget();
}
}
20 changes: 9 additions & 11 deletions crates/bevy_winit/src/winit_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,15 @@ impl WinitWindows {
use wasm_bindgen::JsCast;
use winit::platform::web::WindowBuilderExtWebSys;

if let Some(selector) = &window_descriptor.canvas {
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let canvas = document
.query_selector(&selector)
.expect("Cannot query for canvas element.");
if let Some(canvas) = canvas {
let canvas = canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok();
winit_window_builder = winit_window_builder.with_canvas(canvas);
} else {
panic!("Cannot find element: {}.", selector);
if let Some(canvas) = &window_descriptor.canvas {
match canvas {
bevy_window::Canvas::HtmlCanvas(canvas) => {
let canvas = canvas.dyn_ref::<web_sys::HtmlCanvasElement>().cloned();
winit_window_builder = winit_window_builder.with_canvas(canvas);
}
bevy_window::Canvas::OffscreenCanvas(_) => {
panic!("winit does not support OffscreenCanvas.")
}
}
}
}
Expand Down

0 comments on commit d491071

Please sign in to comment.