mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 05:10:03 -04:00
Add Shape::Callback to do custom rendering inside of an egui UI (#1351)
* Add Shape::Callback to do custom rendering inside of an egui UI * Use Rc<glow::Context> everywhere * Remove trait WebPainter * Add glow::Context to epi::App::setup
This commit is contained in:
@@ -1,18 +1,10 @@
|
||||
use crate::*;
|
||||
use crate::{glow_wrapping::WrappedGlowPainter, *};
|
||||
|
||||
use egui::TexturesDelta;
|
||||
pub use egui::{pos2, Color32};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
fn create_painter(canvas_id: &str) -> Result<Box<dyn WebPainter>, JsValue> {
|
||||
Ok(Box::new(
|
||||
crate::glow_wrapping::WrappedGlowPainter::new(canvas_id).map_err(JsValue::from)?,
|
||||
))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Data gathered between frames.
|
||||
#[derive(Default)]
|
||||
pub struct WebInput {
|
||||
@@ -140,7 +132,7 @@ fn test_parse_query() {
|
||||
pub struct AppRunner {
|
||||
pub(crate) frame: epi::Frame,
|
||||
egui_ctx: egui::Context,
|
||||
painter: Box<dyn WebPainter>,
|
||||
painter: WrappedGlowPainter,
|
||||
pub(crate) input: WebInput,
|
||||
app: Box<dyn epi::App>,
|
||||
pub(crate) needs_repaint: std::sync::Arc<NeedRepaint>,
|
||||
@@ -154,7 +146,7 @@ pub struct AppRunner {
|
||||
|
||||
impl AppRunner {
|
||||
pub fn new(canvas_id: &str, app: Box<dyn epi::App>) -> Result<Self, JsValue> {
|
||||
let painter = create_painter(canvas_id)?;
|
||||
let painter = WrappedGlowPainter::new(canvas_id).map_err(JsValue::from)?;
|
||||
|
||||
let prefer_dark_mode = crate::prefer_dark_mode();
|
||||
|
||||
@@ -162,7 +154,7 @@ impl AppRunner {
|
||||
|
||||
let frame = epi::Frame::new(epi::backend::FrameData {
|
||||
info: epi::IntegrationInfo {
|
||||
name: painter.name(),
|
||||
name: "egui_web",
|
||||
web_info: Some(epi::WebInfo {
|
||||
location: web_location(),
|
||||
}),
|
||||
@@ -201,11 +193,10 @@ impl AppRunner {
|
||||
|
||||
runner.input.raw.max_texture_side = Some(runner.painter.max_texture_side());
|
||||
|
||||
{
|
||||
runner
|
||||
.app
|
||||
.setup(&runner.egui_ctx, &runner.frame, Some(&runner.storage));
|
||||
}
|
||||
let gl = runner.painter.painter.gl();
|
||||
runner
|
||||
.app
|
||||
.setup(&runner.egui_ctx, &runner.frame, Some(&runner.storage), gl);
|
||||
|
||||
Ok(runner)
|
||||
}
|
||||
@@ -245,7 +236,7 @@ impl AppRunner {
|
||||
/// Returns `true` if egui requests a repaint.
|
||||
///
|
||||
/// Call [`Self::paint`] later to paint
|
||||
pub fn logic(&mut self) -> Result<(bool, Vec<egui::ClippedMesh>), JsValue> {
|
||||
pub fn logic(&mut self) -> Result<(bool, Vec<egui::ClippedPrimitive>), JsValue> {
|
||||
let frame_start = now_sec();
|
||||
|
||||
resize_canvas_to_screen_size(self.canvas_id(), self.app.max_size_points());
|
||||
@@ -264,7 +255,7 @@ impl AppRunner {
|
||||
|
||||
self.handle_platform_output(platform_output);
|
||||
self.textures_delta.append(textures_delta);
|
||||
let clipped_meshes = self.egui_ctx.tessellate(shapes);
|
||||
let clipped_primitives = self.egui_ctx.tessellate(shapes);
|
||||
|
||||
{
|
||||
let app_output = self.frame.take_app_output();
|
||||
@@ -278,17 +269,17 @@ impl AppRunner {
|
||||
}
|
||||
|
||||
self.frame.lock().info.cpu_usage = Some((now_sec() - frame_start) as f32);
|
||||
Ok((needs_repaint, clipped_meshes))
|
||||
Ok((needs_repaint, clipped_primitives))
|
||||
}
|
||||
|
||||
/// Paint the results of the last call to [`Self::logic`].
|
||||
pub fn paint(&mut self, clipped_meshes: Vec<egui::ClippedMesh>) -> Result<(), JsValue> {
|
||||
pub fn paint(&mut self, clipped_primitives: &[egui::ClippedPrimitive]) -> Result<(), JsValue> {
|
||||
let textures_delta = std::mem::take(&mut self.textures_delta);
|
||||
|
||||
self.painter.clear(self.app.clear_color());
|
||||
|
||||
self.painter.paint_and_update_textures(
|
||||
clipped_meshes,
|
||||
clipped_primitives,
|
||||
self.egui_ctx.pixels_per_point(),
|
||||
&textures_delta,
|
||||
)?;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use egui::{ClippedMesh, Rgba};
|
||||
use egui::{ClippedPrimitive, Rgba};
|
||||
use egui_glow::glow;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::JsValue;
|
||||
@@ -7,7 +7,6 @@ use web_sys::HtmlCanvasElement;
|
||||
use web_sys::{WebGl2RenderingContext, WebGlRenderingContext};
|
||||
|
||||
pub(crate) struct WrappedGlowPainter {
|
||||
pub(crate) glow_ctx: glow::Context,
|
||||
pub(crate) canvas: HtmlCanvasElement,
|
||||
pub(crate) canvas_id: String,
|
||||
pub(crate) painter: egui_glow::Painter,
|
||||
@@ -17,14 +16,14 @@ impl WrappedGlowPainter {
|
||||
pub fn new(canvas_id: &str) -> Result<Self, String> {
|
||||
let canvas = crate::canvas_element_or_die(canvas_id);
|
||||
|
||||
let (glow_ctx, shader_prefix) = init_glow_context_from_canvas(&canvas)?;
|
||||
let (gl, shader_prefix) = init_glow_context_from_canvas(&canvas)?;
|
||||
let gl = std::rc::Rc::new(gl);
|
||||
|
||||
let dimension = [canvas.width() as i32, canvas.height() as i32];
|
||||
let painter = egui_glow::Painter::new(&glow_ctx, Some(dimension), shader_prefix)
|
||||
let painter = egui_glow::Painter::new(gl, Some(dimension), shader_prefix)
|
||||
.map_err(|error| format!("Error starting glow painter: {}", error))?;
|
||||
|
||||
Ok(Self {
|
||||
glow_ctx,
|
||||
canvas,
|
||||
canvas_id: canvas_id.to_owned(),
|
||||
painter,
|
||||
@@ -32,44 +31,55 @@ impl WrappedGlowPainter {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::WebPainter for WrappedGlowPainter {
|
||||
fn name(&self) -> &'static str {
|
||||
"egui_web"
|
||||
}
|
||||
|
||||
fn max_texture_side(&self) -> usize {
|
||||
impl WrappedGlowPainter {
|
||||
pub fn max_texture_side(&self) -> usize {
|
||||
self.painter.max_texture_side()
|
||||
}
|
||||
|
||||
fn canvas_id(&self) -> &str {
|
||||
pub fn canvas_id(&self) -> &str {
|
||||
&self.canvas_id
|
||||
}
|
||||
|
||||
fn set_texture(&mut self, tex_id: egui::TextureId, delta: &egui::epaint::ImageDelta) {
|
||||
self.painter.set_texture(&self.glow_ctx, tex_id, delta);
|
||||
pub fn set_texture(&mut self, tex_id: egui::TextureId, delta: &egui::epaint::ImageDelta) {
|
||||
self.painter.set_texture(tex_id, delta);
|
||||
}
|
||||
|
||||
fn free_texture(&mut self, tex_id: egui::TextureId) {
|
||||
self.painter.free_texture(&self.glow_ctx, tex_id);
|
||||
pub fn free_texture(&mut self, tex_id: egui::TextureId) {
|
||||
self.painter.free_texture(tex_id);
|
||||
}
|
||||
|
||||
fn clear(&mut self, clear_color: Rgba) {
|
||||
pub fn clear(&mut self, clear_color: Rgba) {
|
||||
let canvas_dimension = [self.canvas.width(), self.canvas.height()];
|
||||
egui_glow::painter::clear(&self.glow_ctx, canvas_dimension, clear_color)
|
||||
egui_glow::painter::clear(self.painter.gl(), canvas_dimension, clear_color)
|
||||
}
|
||||
|
||||
fn paint_meshes(
|
||||
pub fn paint_primitives(
|
||||
&mut self,
|
||||
clipped_meshes: Vec<ClippedMesh>,
|
||||
clipped_primitives: &[ClippedPrimitive],
|
||||
pixels_per_point: f32,
|
||||
) -> Result<(), JsValue> {
|
||||
let canvas_dimension = [self.canvas.width(), self.canvas.height()];
|
||||
self.painter.paint_meshes(
|
||||
&self.glow_ctx,
|
||||
canvas_dimension,
|
||||
pixels_per_point,
|
||||
clipped_meshes,
|
||||
);
|
||||
self.painter
|
||||
.paint_primitives(canvas_dimension, pixels_per_point, clipped_primitives);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn paint_and_update_textures(
|
||||
&mut self,
|
||||
clipped_primitives: &[egui::ClippedPrimitive],
|
||||
pixels_per_point: f32,
|
||||
textures_delta: &egui::TexturesDelta,
|
||||
) -> Result<(), JsValue> {
|
||||
for (id, image_delta) in &textures_delta.set {
|
||||
self.set_texture(*id, image_delta);
|
||||
}
|
||||
|
||||
self.paint_primitives(clipped_primitives, pixels_per_point)?;
|
||||
|
||||
for &id in &textures_delta.free {
|
||||
self.free_texture(id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -115,9 +125,9 @@ fn init_webgl1(canvas: &HtmlCanvasElement) -> Option<(glow::Context, &'static st
|
||||
""
|
||||
};
|
||||
|
||||
let glow_ctx = glow::Context::from_webgl1_context(gl1_ctx);
|
||||
let gl = glow::Context::from_webgl1_context(gl1_ctx);
|
||||
|
||||
Some((glow_ctx, shader_prefix))
|
||||
Some((gl, shader_prefix))
|
||||
}
|
||||
|
||||
fn init_webgl2(canvas: &HtmlCanvasElement) -> Option<(glow::Context, &'static str)> {
|
||||
@@ -131,10 +141,10 @@ fn init_webgl2(canvas: &HtmlCanvasElement) -> Option<(glow::Context, &'static st
|
||||
let gl2_ctx = gl2_ctx
|
||||
.dyn_into::<web_sys::WebGl2RenderingContext>()
|
||||
.unwrap();
|
||||
let glow_ctx = glow::Context::from_webgl2_context(gl2_ctx);
|
||||
let gl = glow::Context::from_webgl2_context(gl2_ctx);
|
||||
let shader_prefix = "";
|
||||
|
||||
Some((glow_ctx, shader_prefix))
|
||||
Some((gl, shader_prefix))
|
||||
}
|
||||
|
||||
trait DummyWebGLConstructor {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
pub mod backend;
|
||||
mod glow_wrapping;
|
||||
mod input;
|
||||
mod painter;
|
||||
pub mod screen_reader;
|
||||
mod text_agent;
|
||||
|
||||
@@ -28,7 +27,6 @@ pub use wasm_bindgen;
|
||||
pub use web_sys;
|
||||
|
||||
use input::*;
|
||||
pub use painter::WebPainter;
|
||||
use web_sys::EventTarget;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
@@ -349,8 +347,8 @@ fn paint_and_schedule(runner_ref: &AppRunnerRef, panicked: Arc<AtomicBool>) -> R
|
||||
fn paint_if_needed(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
|
||||
let mut runner_lock = runner_ref.lock();
|
||||
if runner_lock.needs_repaint.fetch_and_clear() {
|
||||
let (needs_repaint, clipped_meshes) = runner_lock.logic()?;
|
||||
runner_lock.paint(clipped_meshes)?;
|
||||
let (needs_repaint, clipped_primitives) = runner_lock.logic()?;
|
||||
runner_lock.paint(&clipped_primitives)?;
|
||||
if needs_repaint {
|
||||
runner_lock.needs_repaint.set_true();
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
use wasm_bindgen::prelude::JsValue;
|
||||
|
||||
/// What is needed to paint egui.
|
||||
pub trait WebPainter {
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Max size of one side of a texture.
|
||||
fn max_texture_side(&self) -> usize;
|
||||
|
||||
/// id of the canvas html element containing the rendering
|
||||
fn canvas_id(&self) -> &str;
|
||||
|
||||
fn set_texture(&mut self, tex_id: egui::TextureId, delta: &egui::epaint::ImageDelta);
|
||||
|
||||
fn free_texture(&mut self, tex_id: egui::TextureId);
|
||||
|
||||
fn clear(&mut self, clear_color: egui::Rgba);
|
||||
|
||||
fn paint_meshes(
|
||||
&mut self,
|
||||
clipped_meshes: Vec<egui::ClippedMesh>,
|
||||
pixels_per_point: f32,
|
||||
) -> Result<(), JsValue>;
|
||||
|
||||
fn paint_and_update_textures(
|
||||
&mut self,
|
||||
clipped_meshes: Vec<egui::ClippedMesh>,
|
||||
pixels_per_point: f32,
|
||||
textures_delta: &egui::TexturesDelta,
|
||||
) -> Result<(), JsValue> {
|
||||
for (id, image_delta) in &textures_delta.set {
|
||||
self.set_texture(*id, image_delta);
|
||||
}
|
||||
|
||||
self.paint_meshes(clipped_meshes, pixels_per_point)?;
|
||||
|
||||
for &id in &textures_delta.free {
|
||||
self.free_texture(id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user