mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 13:20:05 -04:00
Merge branch 'master' of https://github.com/emilk/egui into multiples_viewports
This commit is contained in:
@@ -57,7 +57,7 @@ impl AppRunner {
|
||||
egui_ctx.set_os(egui::os::OperatingSystem::from_user_agent(
|
||||
&super::user_agent().unwrap_or_default(),
|
||||
));
|
||||
super::load_memory(&egui_ctx);
|
||||
super::storage::load_memory(&egui_ctx);
|
||||
|
||||
let theme = system_theme.unwrap_or(web_options.default_theme);
|
||||
egui_ctx.set_visuals(theme.egui_visuals());
|
||||
@@ -141,7 +141,7 @@ impl AppRunner {
|
||||
|
||||
pub fn save(&mut self) {
|
||||
if self.app.persist_egui_memory() {
|
||||
super::save_memory(&self.egui_ctx);
|
||||
super::storage::save_memory(&self.egui_ctx);
|
||||
}
|
||||
if let Some(storage) = self.frame.storage_mut() {
|
||||
self.app.save(storage);
|
||||
@@ -267,11 +267,11 @@ struct LocalStorage {}
|
||||
|
||||
impl epi::Storage for LocalStorage {
|
||||
fn get_string(&self, key: &str) -> Option<String> {
|
||||
super::local_storage_get(key)
|
||||
super::storage::local_storage_get(key)
|
||||
}
|
||||
|
||||
fn set_string(&mut self, key: &str, value: String) {
|
||||
super::local_storage_set(key, &value);
|
||||
super::storage::local_storage_set(key, &value);
|
||||
}
|
||||
|
||||
fn flush(&mut self) {}
|
||||
|
||||
@@ -10,13 +10,14 @@ use super::percent_decode;
|
||||
|
||||
/// Data gathered between frames.
|
||||
#[derive(Default)]
|
||||
pub struct WebInput {
|
||||
pub(crate) struct WebInput {
|
||||
/// Required because we don't get a position on touched
|
||||
pub latest_touch_pos: Option<egui::Pos2>,
|
||||
|
||||
/// Required to maintain a stable touch position for multi-touch gestures.
|
||||
pub latest_touch_pos_id: Option<egui::TouchId>,
|
||||
|
||||
/// The raw input to `egui`.
|
||||
pub raw: egui::RawInput,
|
||||
}
|
||||
|
||||
@@ -41,10 +42,8 @@ impl WebInput {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
use std::sync::atomic::Ordering::SeqCst;
|
||||
|
||||
/// Stores when to do the next repaint.
|
||||
pub struct NeedRepaint(Mutex<f64>);
|
||||
pub(crate) struct NeedRepaint(Mutex<f64>);
|
||||
|
||||
impl Default for NeedRepaint {
|
||||
fn default() -> Self {
|
||||
@@ -74,30 +73,14 @@ impl NeedRepaint {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IsDestroyed(std::sync::atomic::AtomicBool);
|
||||
|
||||
impl Default for IsDestroyed {
|
||||
fn default() -> Self {
|
||||
Self(false.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl IsDestroyed {
|
||||
pub fn fetch(&self) -> bool {
|
||||
self.0.load(SeqCst)
|
||||
}
|
||||
|
||||
pub fn set_true(&self) {
|
||||
self.0.store(true, SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// The User-Agent of the user's browser.
|
||||
pub fn user_agent() -> Option<String> {
|
||||
web_sys::window()?.navigator().user_agent().ok()
|
||||
}
|
||||
|
||||
/// Get the [`epi::Location`] from the browser.
|
||||
pub fn web_location() -> epi::Location {
|
||||
let location = web_sys::window().unwrap().location();
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ fn paint_if_needed(runner: &mut AppRunner) -> Result<(), JsValue> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn request_animation_frame(runner_ref: WebRunner) -> Result<(), JsValue> {
|
||||
pub(crate) fn request_animation_frame(runner_ref: WebRunner) -> Result<(), JsValue> {
|
||||
let window = web_sys::window().unwrap();
|
||||
let closure = Closure::once(move || paint_and_schedule(&runner_ref));
|
||||
window.request_animation_frame(closure.as_ref().unchecked_ref())?;
|
||||
@@ -39,7 +39,7 @@ pub fn request_animation_frame(runner_ref: WebRunner) -> Result<(), JsValue> {
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
pub fn install_document_events(runner_ref: &WebRunner) -> Result<(), JsValue> {
|
||||
pub(crate) fn install_document_events(runner_ref: &WebRunner) -> Result<(), JsValue> {
|
||||
let document = web_sys::window().unwrap().document().unwrap();
|
||||
|
||||
{
|
||||
@@ -189,7 +189,7 @@ pub fn install_document_events(runner_ref: &WebRunner) -> Result<(), JsValue> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn install_window_events(runner_ref: &WebRunner) -> Result<(), JsValue> {
|
||||
pub(crate) fn install_window_events(runner_ref: &WebRunner) -> Result<(), JsValue> {
|
||||
let window = web_sys::window().unwrap();
|
||||
|
||||
// Save-on-close
|
||||
@@ -211,7 +211,7 @@ pub fn install_window_events(runner_ref: &WebRunner) -> Result<(), JsValue> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn install_color_scheme_change_event(runner_ref: &WebRunner) -> Result<(), JsValue> {
|
||||
pub(crate) fn install_color_scheme_change_event(runner_ref: &WebRunner) -> Result<(), JsValue> {
|
||||
let window = web_sys::window().unwrap();
|
||||
|
||||
if let Some(media_query_list) = prefers_color_scheme_dark(&window)? {
|
||||
@@ -230,7 +230,7 @@ pub fn install_color_scheme_change_event(runner_ref: &WebRunner) -> Result<(), J
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValue> {
|
||||
pub(crate) fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValue> {
|
||||
let canvas = canvas_element(runner_ref.try_lock().unwrap().canvas_id()).unwrap();
|
||||
|
||||
{
|
||||
|
||||
@@ -3,16 +3,20 @@
|
||||
#![allow(clippy::missing_errors_doc)] // So many `-> Result<_, JsValue>`
|
||||
|
||||
mod app_runner;
|
||||
pub mod backend;
|
||||
mod backend;
|
||||
mod events;
|
||||
mod input;
|
||||
mod panic_handler;
|
||||
pub mod screen_reader;
|
||||
pub mod storage;
|
||||
mod text_agent;
|
||||
mod web_logger;
|
||||
mod web_runner;
|
||||
|
||||
/// Access to the browser screen reader.
|
||||
pub mod screen_reader;
|
||||
|
||||
/// Access to local browser storage.
|
||||
pub mod storage;
|
||||
|
||||
pub(crate) use app_runner::AppRunner;
|
||||
pub use panic_handler::{PanicHandler, PanicSummary};
|
||||
pub use web_logger::WebLogger;
|
||||
@@ -34,8 +38,6 @@ mod web_painter_wgpu;
|
||||
pub(crate) type ActiveWebPainter = web_painter_wgpu::WebPainterWgpu;
|
||||
|
||||
pub use backend::*;
|
||||
pub use events::*;
|
||||
pub use storage::*;
|
||||
|
||||
use egui::Vec2;
|
||||
use wasm_bindgen::prelude::*;
|
||||
@@ -59,15 +61,9 @@ pub fn now_sec() -> f64 {
|
||||
/ 1000.0
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn screen_size_in_native_points() -> Option<egui::Vec2> {
|
||||
let window = web_sys::window()?;
|
||||
Some(egui::vec2(
|
||||
window.inner_width().ok()?.as_f64()? as f32,
|
||||
window.inner_height().ok()?.as_f64()? as f32,
|
||||
))
|
||||
}
|
||||
|
||||
/// The native GUI scale factor, taking into account the browser zoom.
|
||||
///
|
||||
/// Corresponds to [`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) in JavaScript.
|
||||
pub fn native_pixels_per_point() -> f32 {
|
||||
let pixels_per_point = web_sys::window().unwrap().device_pixel_ratio() as f32;
|
||||
if pixels_per_point > 0.0 && pixels_per_point.is_finite() {
|
||||
@@ -77,6 +73,9 @@ pub fn native_pixels_per_point() -> f32 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Ask the browser about the preferred system theme.
|
||||
///
|
||||
/// `None` means unknown.
|
||||
pub fn system_theme() -> Option<Theme> {
|
||||
let dark_mode = prefers_color_scheme_dark(&web_sys::window()?)
|
||||
.ok()??
|
||||
@@ -96,13 +95,13 @@ fn theme_from_dark_mode(dark_mode: bool) -> Theme {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn canvas_element(canvas_id: &str) -> Option<web_sys::HtmlCanvasElement> {
|
||||
fn canvas_element(canvas_id: &str) -> Option<web_sys::HtmlCanvasElement> {
|
||||
let document = web_sys::window()?.document()?;
|
||||
let canvas = document.get_element_by_id(canvas_id)?;
|
||||
canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok()
|
||||
}
|
||||
|
||||
pub fn canvas_element_or_die(canvas_id: &str) -> web_sys::HtmlCanvasElement {
|
||||
fn canvas_element_or_die(canvas_id: &str) -> web_sys::HtmlCanvasElement {
|
||||
canvas_element(canvas_id)
|
||||
.unwrap_or_else(|| panic!("Failed to find canvas with id {canvas_id:?}"))
|
||||
}
|
||||
@@ -114,7 +113,7 @@ fn canvas_origin(canvas_id: &str) -> egui::Pos2 {
|
||||
egui::pos2(rect.left() as f32, rect.top() as f32)
|
||||
}
|
||||
|
||||
pub fn canvas_size_in_points(canvas_id: &str) -> egui::Vec2 {
|
||||
fn canvas_size_in_points(canvas_id: &str) -> egui::Vec2 {
|
||||
let canvas = canvas_element(canvas_id).unwrap();
|
||||
let pixels_per_point = native_pixels_per_point();
|
||||
egui::vec2(
|
||||
@@ -123,7 +122,7 @@ pub fn canvas_size_in_points(canvas_id: &str) -> egui::Vec2 {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn resize_canvas_to_screen_size(canvas_id: &str, max_size_points: egui::Vec2) -> Option<()> {
|
||||
fn resize_canvas_to_screen_size(canvas_id: &str, max_size_points: egui::Vec2) -> Option<()> {
|
||||
let canvas = canvas_element(canvas_id)?;
|
||||
let parent = canvas.parent_element()?;
|
||||
|
||||
@@ -178,7 +177,8 @@ pub fn resize_canvas_to_screen_size(canvas_id: &str, max_size_points: egui::Vec2
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
pub fn set_cursor_icon(cursor: egui::CursorIcon) -> Option<()> {
|
||||
/// Set the cursor icon.
|
||||
fn set_cursor_icon(cursor: egui::CursorIcon) -> Option<()> {
|
||||
let document = web_sys::window()?.document()?;
|
||||
document
|
||||
.body()?
|
||||
@@ -187,8 +187,9 @@ pub fn set_cursor_icon(cursor: egui::CursorIcon) -> Option<()> {
|
||||
.ok()
|
||||
}
|
||||
|
||||
/// Set the clipboard text.
|
||||
#[cfg(web_sys_unstable_apis)]
|
||||
pub fn set_clipboard_text(s: &str) {
|
||||
fn set_clipboard_text(s: &str) {
|
||||
if let Some(window) = web_sys::window() {
|
||||
if let Some(clipboard) = window.navigator().clipboard() {
|
||||
let promise = clipboard.write_text(s);
|
||||
@@ -245,6 +246,7 @@ fn cursor_web_name(cursor: egui::CursorIcon) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
/// Open the given url in the browser.
|
||||
pub fn open_url(url: &str, new_tab: bool) -> Option<()> {
|
||||
let name = if new_tab { "_blank" } else { "_self" };
|
||||
|
||||
@@ -267,6 +269,7 @@ pub fn location_hash() -> String {
|
||||
)
|
||||
}
|
||||
|
||||
/// Percent-decodes a string.
|
||||
pub fn percent_decode(s: &str) -> String {
|
||||
percent_encoding::percent_decode_str(s)
|
||||
.decode_utf8_lossy()
|
||||
|
||||
@@ -65,16 +65,19 @@ pub struct PanicSummary {
|
||||
}
|
||||
|
||||
impl PanicSummary {
|
||||
/// Construct a summary from a panic.
|
||||
pub fn new(info: &std::panic::PanicInfo<'_>) -> Self {
|
||||
let message = info.to_string();
|
||||
let callstack = Error::new().stack();
|
||||
Self { message, callstack }
|
||||
}
|
||||
|
||||
/// The panic message.
|
||||
pub fn message(&self) -> String {
|
||||
self.message.clone()
|
||||
}
|
||||
|
||||
/// The backtrace.
|
||||
pub fn callstack(&self) -> String {
|
||||
self.callstack.clone()
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/// Screen reader support.
|
||||
pub struct ScreenReader {
|
||||
#[cfg(feature = "tts")]
|
||||
tts: Option<tts::Tts>,
|
||||
@@ -29,10 +30,12 @@ impl Default for ScreenReader {
|
||||
}
|
||||
|
||||
impl ScreenReader {
|
||||
/// Speak the given text out loud.
|
||||
#[cfg(not(feature = "tts"))]
|
||||
#[allow(clippy::unused_self)]
|
||||
pub fn speak(&mut self, _text: &str) {}
|
||||
|
||||
/// Speak the given text out loud.
|
||||
#[cfg(feature = "tts")]
|
||||
pub fn speak(&mut self, text: &str) {
|
||||
if text.is_empty() {
|
||||
|
||||
@@ -2,16 +2,18 @@ fn local_storage() -> Option<web_sys::Storage> {
|
||||
web_sys::window()?.local_storage().ok()?
|
||||
}
|
||||
|
||||
/// Read data from local storage.
|
||||
pub fn local_storage_get(key: &str) -> Option<String> {
|
||||
local_storage().map(|storage| storage.get_item(key).ok())??
|
||||
}
|
||||
|
||||
/// Write data to local storage.
|
||||
pub fn local_storage_set(key: &str, value: &str) {
|
||||
local_storage().map(|storage| storage.set_item(key, value));
|
||||
}
|
||||
|
||||
#[cfg(feature = "persistence")]
|
||||
pub fn load_memory(ctx: &egui::Context) {
|
||||
pub(crate) fn load_memory(ctx: &egui::Context) {
|
||||
if let Some(memory_string) = local_storage_get("egui_memory_ron") {
|
||||
match ron::from_str(&memory_string) {
|
||||
Ok(memory) => {
|
||||
@@ -25,10 +27,10 @@ pub fn load_memory(ctx: &egui::Context) {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "persistence"))]
|
||||
pub fn load_memory(_: &egui::Context) {}
|
||||
pub(crate) fn load_memory(_: &egui::Context) {}
|
||||
|
||||
#[cfg(feature = "persistence")]
|
||||
pub fn save_memory(ctx: &egui::Context) {
|
||||
pub(crate) fn save_memory(ctx: &egui::Context) {
|
||||
match ctx.memory(|mem| ron::to_string(mem)) {
|
||||
Ok(ron) => {
|
||||
local_storage_set("egui_memory_ron", &ron);
|
||||
@@ -40,4 +42,4 @@ pub fn save_memory(ctx: &egui::Context) {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "persistence"))]
|
||||
pub fn save_memory(_: &egui::Context) {}
|
||||
pub(crate) fn save_memory(_: &egui::Context) {}
|
||||
|
||||
@@ -4,12 +4,14 @@ pub struct WebLogger {
|
||||
}
|
||||
|
||||
impl WebLogger {
|
||||
/// Pipe all [`log`] events to the web console.
|
||||
/// Install a new `WebLogger`, piping all [`log`] events to the web console.
|
||||
pub fn init(filter: log::LevelFilter) -> Result<(), log::SetLoggerError> {
|
||||
log::set_max_level(filter);
|
||||
log::set_boxed_logger(Box::new(WebLogger::new(filter)))
|
||||
}
|
||||
|
||||
/// Create a new [`WebLogger`] with the given filter,
|
||||
/// but don't install it.
|
||||
pub fn new(filter: log::LevelFilter) -> Self {
|
||||
Self { filter }
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ pub struct WebRunner {
|
||||
}
|
||||
|
||||
impl WebRunner {
|
||||
// Will install a panic handler that will catch and log any panics
|
||||
/// Will install a panic handler that will catch and log any panics
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
#[cfg(not(web_sys_unstable_apis))]
|
||||
@@ -102,6 +102,7 @@ impl WebRunner {
|
||||
}
|
||||
}
|
||||
|
||||
/// Shut down eframe and clean up resources.
|
||||
pub fn destroy(&self) {
|
||||
self.unsubscribe_from_all_events();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user