Skip to content

Commit

Permalink
Merge branch 'master' into raf-0
Browse files Browse the repository at this point in the history
  • Loading branch information
jprochazk committed May 27, 2024
2 parents f5c2dd8 + 759c8fd commit d5a5387
Show file tree
Hide file tree
Showing 16 changed files with 125 additions and 67 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ members = [
"crates/epaint",

"examples/*",
"tests/*",

"xtask",
]
Expand Down
7 changes: 7 additions & 0 deletions crates/eframe/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,14 @@ web-sys = { workspace = true, features = [
"MediaQueryListEvent",
"MouseEvent",
"Navigator",
"Node",
"NodeList",
"Performance",
"ResizeObserver",
"ResizeObserverEntry",
"ResizeObserverBoxOptions",
"ResizeObserverOptions",
"ResizeObserverSize",
"Storage",
"Touch",
"TouchEvent",
Expand Down
7 changes: 0 additions & 7 deletions crates/eframe/src/epi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,11 +464,6 @@ pub struct WebOptions {
/// Configures wgpu instance/device/adapter/surface creation and renderloop.
#[cfg(feature = "wgpu")]
pub wgpu_options: egui_wgpu::WgpuConfiguration,

/// The size limit of the web app canvas.
///
/// By default the max size is [`egui::Vec2::INFINITY`], i.e. unlimited.
pub max_size_points: egui::Vec2,
}

#[cfg(target_arch = "wasm32")]
Expand All @@ -484,8 +479,6 @@ impl Default for WebOptions {

#[cfg(feature = "wgpu")]
wgpu_options: egui_wgpu::WgpuConfiguration::default(),

max_size_points: egui::Vec2::INFINITY,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/eframe/src/web/app_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{epi, App};
use super::{now_sec, web_painter::WebPainter, NeedRepaint};

pub struct AppRunner {
#[allow(dead_code)]
web_options: crate::WebOptions,
pub(crate) frame: epi::Frame,
egui_ctx: egui::Context,
Expand Down Expand Up @@ -184,7 +185,6 @@ impl AppRunner {
///
/// The result can be painted later with a call to [`Self::run_and_paint`] or [`Self::paint`].
pub fn logic(&mut self) {
super::resize_canvas_to_screen_size(self.canvas(), self.web_options.max_size_points);
let canvas_size = super::canvas_size_in_points(self.canvas(), self.egui_ctx());
let raw_input = self.input.new_frame(canvas_size);

Expand Down
79 changes: 78 additions & 1 deletion crates/eframe/src/web/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ pub(crate) fn install_window_events(runner_ref: &WebRunner) -> Result<(), JsValu
runner.save();
})?;

for event_name in &["load", "pagehide", "pageshow", "resize"] {
// NOTE: resize is handled by `ResizeObserver` below
for event_name in &["load", "pagehide", "pageshow"] {
runner_ref.add_event_listener(&window, event_name, move |_: web_sys::Event, runner| {
// log::debug!("{event_name:?}");
runner.needs_repaint.repaint_asap();
Expand Down Expand Up @@ -590,3 +591,79 @@ pub(crate) fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValu

Ok(())
}

pub(crate) fn install_resize_observer(runner_ref: &WebRunner) -> Result<(), JsValue> {
let closure = Closure::wrap(Box::new({
let runner_ref = runner_ref.clone();
move |entries: js_sys::Array| {
// Only call the wrapped closure if the egui code has not panicked
if let Some(mut runner_lock) = runner_ref.try_lock() {
let canvas = runner_lock.canvas();
let (width, height) = match get_display_size(&entries) {
Ok(v) => v,
Err(err) => {
log::error!("{}", super::string_from_js_value(&err));
return;
}
};
canvas.set_width(width);
canvas.set_height(height);

// force an immediate repaint
runner_lock.needs_repaint.repaint_asap();
paint_if_needed(&mut runner_lock);
}
}
}) as Box<dyn FnMut(js_sys::Array)>);

let observer = web_sys::ResizeObserver::new(closure.as_ref().unchecked_ref())?;
let mut options = web_sys::ResizeObserverOptions::new();
options.box_(web_sys::ResizeObserverBoxOptions::ContentBox);
if let Some(runner_lock) = runner_ref.try_lock() {
observer.observe_with_options(runner_lock.canvas(), &options);
drop(runner_lock);
runner_ref.set_resize_observer(observer, closure);
}

Ok(())
}

// Code ported to Rust from:
// https://webglfundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
fn get_display_size(resize_observer_entries: &js_sys::Array) -> Result<(u32, u32), JsValue> {
let width;
let height;
let mut dpr = web_sys::window().unwrap().device_pixel_ratio();

let entry: web_sys::ResizeObserverEntry = resize_observer_entries.at(0).dyn_into()?;
if JsValue::from_str("devicePixelContentBoxSize").js_in(entry.as_ref()) {
// NOTE: Only this path gives the correct answer for most browsers.
// Unfortunately this doesn't work perfectly everywhere.
let size: web_sys::ResizeObserverSize =
entry.device_pixel_content_box_size().at(0).dyn_into()?;
width = size.inline_size();
height = size.block_size();
dpr = 1.0; // no need to apply
} else if JsValue::from_str("contentBoxSize").js_in(entry.as_ref()) {
let content_box_size = entry.content_box_size();
let idx0 = content_box_size.at(0);
if !idx0.is_undefined() {
let size: web_sys::ResizeObserverSize = idx0.dyn_into()?;
width = size.inline_size();
height = size.block_size();
} else {
// legacy
let size = JsValue::clone(content_box_size.as_ref());
let size: web_sys::ResizeObserverSize = size.dyn_into()?;
width = size.inline_size();
height = size.block_size();
}
} else {
// legacy
let content_rect = entry.content_rect();
width = content_rect.width();
height = content_rect.height();
}

Ok(((width.round() * dpr) as u32, (height.round() * dpr) as u32))
}
51 changes: 0 additions & 51 deletions crates/eframe/src/web/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ pub(crate) type ActiveWebPainter = web_painter_wgpu::WebPainterWgpu;

pub use backend::*;

use egui::Vec2;
use wasm_bindgen::prelude::*;
use web_sys::MediaQueryList;

Expand Down Expand Up @@ -124,56 +123,6 @@ fn canvas_size_in_points(canvas: &web_sys::HtmlCanvasElement, ctx: &egui::Contex
)
}

fn resize_canvas_to_screen_size(
canvas: &web_sys::HtmlCanvasElement,
max_size_points: egui::Vec2,
) -> Option<()> {
let parent = canvas.parent_element()?;

// In this function we use "pixel" to mean physical pixel,
// and "point" to mean "logical CSS pixel".
let pixels_per_point = native_pixels_per_point();

// Prefer the client width and height so that if the parent
// element is resized that the egui canvas resizes appropriately.
let parent_size_points = Vec2 {
x: parent.client_width() as f32,
y: parent.client_height() as f32,
};

if parent_size_points.x <= 0.0 || parent_size_points.y <= 0.0 {
log::error!("The parent element of the egui canvas is {}x{}. Try adding `html, body {{ height: 100%; width: 100% }}` to your CSS!", parent_size_points.x, parent_size_points.y);
}

// We take great care here to ensure the rendered canvas aligns
// perfectly to the physical pixel grid, lest we get blurry text.
// At the time of writing, we get pixel perfection on Chromium and Firefox on Mac,
// but Desktop Safari will be blurry on most zoom levels.
// See https://github.com/emilk/egui/issues/4241 for more.

let canvas_size_pixels = pixels_per_point * parent_size_points.min(max_size_points);

// Make sure that the size is always an even number of pixels,
// otherwise, the page renders blurry on some platforms.
// See https://github.com/emilk/egui/issues/103
let canvas_size_pixels = (canvas_size_pixels / 2.0).round() * 2.0;

let canvas_size_points = canvas_size_pixels / pixels_per_point;

canvas
.style()
.set_property("width", &format!("{}px", canvas_size_points.x))
.ok()?;
canvas
.style()
.set_property("height", &format!("{}px", canvas_size_points.y))
.ok()?;
canvas.set_width(canvas_size_pixels.x as u32);
canvas.set_height(canvas_size_pixels.y as u32);

Some(())
}

// ----------------------------------------------------------------------------

/// Set the cursor icon.
Expand Down
28 changes: 28 additions & 0 deletions crates/eframe/src/web/web_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ pub struct WebRunner {

/// Current animation frame in flight.
frame: Rc<RefCell<Option<AnimationFrameRequest>>>,

resize_observer: Rc<RefCell<Option<ResizeObserverContext>>>,
}

impl WebRunner {
Expand All @@ -45,6 +47,7 @@ impl WebRunner {
runner: Rc::new(RefCell::new(None)),
events_to_unsubscribe: Rc::new(RefCell::new(Default::default())),
frame: Default::default(),
resize_observer: Default::default(),
}
}

Expand Down Expand Up @@ -75,6 +78,8 @@ impl WebRunner {
events::install_color_scheme_change_event(self)?;
}

events::install_resize_observer(self)?;

self.request_animation_frame()?;
}

Expand Down Expand Up @@ -106,6 +111,11 @@ impl WebRunner {
}
}
}

if let Some(context) = self.resize_observer.take() {
context.resize_observer.disconnect();
drop(context.closure);
}
}

/// Shut down eframe and clean up resources.
Expand Down Expand Up @@ -215,6 +225,19 @@ impl WebRunner {

Ok(())
}

pub(crate) fn set_resize_observer(
&self,
resize_observer: web_sys::ResizeObserver,
closure: Closure<dyn FnMut(js_sys::Array)>,
) {
self.resize_observer
.borrow_mut()
.replace(ResizeObserverContext {
resize_observer,
closure,
});
}
}

// ----------------------------------------------------------------------------
Expand All @@ -232,6 +255,11 @@ struct AnimationFrameRequest {
_closure: Closure<dyn FnMut() -> Result<(), JsValue>>,
}

struct ResizeObserverContext {
resize_observer: web_sys::ResizeObserver,
closure: Closure<dyn FnMut(js_sys::Array)>,
}

struct TargetEvent {
target: web_sys::EventTarget,
event_name: String,
Expand Down
2 changes: 1 addition & 1 deletion examples/multiple_viewports/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ Example how to show multiple viewports (native windows) can be created in `egui`
cargo run -p multiple_viewports
```

For a more advanced example, see [../test_viewports].
For a more advanced example, see [../../tests/test_viewports].
Binary file removed examples/test_viewports/screenshot.png
Binary file not shown.
4 changes: 4 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Test apps
Some application to tests various parts of egui and eframe.

At some point it would be nice to have automatic screenshot regression tests for these.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
This is a test of the viewports feature of eframe and egui, where we show off using multiple windows.

For a simple example, see [`multiple_viewports`](../multiple_viewports).

![](screenshot.png)
For a simple example, see [`multiple_viewports`](../../examples/multiple_viewports).
File renamed without changes.
7 changes: 4 additions & 3 deletions web_demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@
margin-left: auto;
display: block;
position: absolute;
top: 0%;
left: 50%;
transform: translate(-50%, 0%);
top: 0;
left: 0;
width: 100%;
height: 100%;
}

.centered {
Expand Down

0 comments on commit d5a5387

Please sign in to comment.