[app] Add demo app slider to change scale of all of Egui
This commit is contained in:
parent
251cde60f0
commit
a4e19d7207
9 changed files with 149 additions and 85 deletions
|
@ -5,6 +5,7 @@
|
|||
* `ui.horizontal(...)` etc returns `Response`
|
||||
* Add ability to override text color with `visuals.override_text_color`
|
||||
* Refactored the interface for `egui::app::App`
|
||||
* Demo App: Add slider to scale all of Egui
|
||||
|
||||
## 0.2.0 - 2020-10-10
|
||||
|
||||
|
|
|
@ -24,12 +24,14 @@ pub trait App {
|
|||
fn on_exit(&mut self, _storage: &mut dyn Storage) {}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WebInfo {
|
||||
/// e.g. "#fragment" part of "www.example.com/index.html#fragment"
|
||||
pub web_location_hash: String,
|
||||
}
|
||||
|
||||
/// Information about the backend passed to the use app each frame.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BackendInfo {
|
||||
/// If the app is running in a Web context, this returns information about the environment.
|
||||
pub web_info: Option<WebInfo>,
|
||||
|
@ -41,14 +43,20 @@ pub struct BackendInfo {
|
|||
/// Local time. Used for the clock in the demo app.
|
||||
/// Set to `None` if you don't know.
|
||||
pub seconds_since_midnight: Option<f64>,
|
||||
|
||||
/// The OS native pixels-per-point
|
||||
pub native_pixels_per_point: Option<f32>,
|
||||
}
|
||||
|
||||
/// Action that can be taken by the user app.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct AppOutput {
|
||||
/// Set to `true` to stop the app.
|
||||
/// This does nothing for web apps.
|
||||
pub quit: bool,
|
||||
|
||||
/// If the app sets this, change the `pixels_per_point` of Egui to this next frame.
|
||||
pub pixels_per_point: Option<f32>,
|
||||
}
|
||||
|
||||
pub trait TextureAllocator {
|
||||
|
|
|
@ -188,6 +188,9 @@ pub struct DemoApp {
|
|||
demo_window: DemoWindow,
|
||||
fractal_clock: FractalClock,
|
||||
|
||||
/// current slider value for current gui scale (backend demo only)
|
||||
pixels_per_point: Option<f32>,
|
||||
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
frame_history: FrameHistory,
|
||||
|
||||
|
@ -320,8 +323,6 @@ impl DemoApp {
|
|||
|
||||
let is_web = info.web_info.is_some();
|
||||
|
||||
let mut options = app::AppOutput::default();
|
||||
|
||||
if is_web {
|
||||
ui.label("Egui is an immediate mode GUI written in Rust, compiled to WebAssembly, rendered with WebGL.");
|
||||
ui.label(
|
||||
|
@ -332,23 +333,17 @@ impl DemoApp {
|
|||
ui.label("Project home page:");
|
||||
ui.hyperlink("https://github.com/emilk/egui");
|
||||
});
|
||||
} else {
|
||||
ui.heading("Egui");
|
||||
options.quit |= ui.button("Quit").clicked;
|
||||
ui.separator();
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
self.run_mode_ui(ui);
|
||||
|
||||
if self.run_mode == RunMode::Continuous {
|
||||
ui.label(format!(
|
||||
"Repainting the UI each frame. FPS: {:.1}",
|
||||
self.frame_history.fps()
|
||||
));
|
||||
} else {
|
||||
ui.label("Only running UI code when there are animations or input");
|
||||
}
|
||||
ui.separator();
|
||||
|
||||
let mut output = app::AppOutput::default();
|
||||
output.pixels_per_point = self.pixels_per_point_ui(ui, info);
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.separator();
|
||||
self.frame_history.ui(ui);
|
||||
|
@ -359,7 +354,41 @@ impl DemoApp {
|
|||
"Show color blend test (debug backend painter)",
|
||||
);
|
||||
|
||||
options
|
||||
if !is_web {
|
||||
output.quit |= ui.button("Quit").clicked;
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn pixels_per_point_ui(&mut self, ui: &mut Ui, info: &app::BackendInfo) -> Option<f32> {
|
||||
self.pixels_per_point = self
|
||||
.pixels_per_point
|
||||
.or(info.native_pixels_per_point)
|
||||
.or_else(|| Some(ui.ctx().pixels_per_point()));
|
||||
if let Some(pixels_per_point) = &mut self.pixels_per_point {
|
||||
ui.add(
|
||||
Slider::f32(pixels_per_point, 0.5..=5.0)
|
||||
.logarithmic(true)
|
||||
.text("Scale (physical pixels per point)"),
|
||||
);
|
||||
if let Some(native_pixels_per_point) = info.native_pixels_per_point {
|
||||
if ui
|
||||
.button(format!(
|
||||
"Reset scale to native value ({:.1})",
|
||||
native_pixels_per_point
|
||||
))
|
||||
.clicked
|
||||
{
|
||||
*pixels_per_point = native_pixels_per_point;
|
||||
}
|
||||
}
|
||||
if !ui.ctx().is_using_mouse() {
|
||||
// We wait until mouse release to activate:
|
||||
return Some(*pixels_per_point);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn run_mode_ui(&mut self, ui: &mut Ui) {
|
||||
|
@ -371,6 +400,15 @@ impl DemoApp {
|
|||
ui.radio_value(run_mode, RunMode::Reactive, "Reactive")
|
||||
.on_hover_text("Repaint when there are animations or input (e.g. mouse movement)");
|
||||
});
|
||||
|
||||
if self.run_mode == RunMode::Continuous {
|
||||
ui.label(format!(
|
||||
"Repainting the UI each frame. FPS: {:.1}",
|
||||
self.frame_history.fps()
|
||||
));
|
||||
} else {
|
||||
ui.label("Only running UI code when there are animations or input");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -388,7 +388,7 @@ impl InputState {
|
|||
ui.label(format!("scroll_delta: {:?} points", scroll_delta));
|
||||
ui.label(format!("screen_size: {:?} points", screen_size));
|
||||
ui.label(format!(
|
||||
"{:?} points for each physical pixel (HDPI factor)",
|
||||
"{:?} physical pixels for each logical point",
|
||||
pixels_per_point
|
||||
));
|
||||
ui.label(format!("time: {:.3} s", time));
|
||||
|
|
|
@ -51,7 +51,10 @@ pub fn run(title: &str, mut storage: FileStorage, mut app: impl App + 'static) -
|
|||
let mut ctx = egui::Context::new();
|
||||
*ctx.memory() = egui::app::get_value(&storage, EGUI_MEMORY_KEY).unwrap_or_default();
|
||||
|
||||
let mut raw_input = make_raw_input(&display);
|
||||
let mut raw_input = egui::RawInput {
|
||||
pixels_per_point: Some(native_pixels_per_point(&display)),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let start_time = Instant::now();
|
||||
let mut previous_frame_time = None;
|
||||
|
@ -62,11 +65,14 @@ pub fn run(title: &str, mut storage: FileStorage, mut app: impl App + 'static) -
|
|||
let mut redraw = || {
|
||||
let egui_start = Instant::now();
|
||||
raw_input.time = start_time.elapsed().as_nanos() as f64 * 1e-9;
|
||||
raw_input.screen_size =
|
||||
screen_size_in_pixels(&display) / raw_input.pixels_per_point.unwrap();
|
||||
|
||||
let backend_info = egui::app::BackendInfo {
|
||||
web_info: None,
|
||||
cpu_usage: previous_frame_time,
|
||||
seconds_since_midnight: Some(seconds_since_midnight()),
|
||||
native_pixels_per_point: Some(native_pixels_per_point(&display)),
|
||||
};
|
||||
|
||||
let mut ui = ctx.begin_frame(raw_input.take());
|
||||
|
@ -75,10 +81,17 @@ pub fn run(title: &str, mut storage: FileStorage, mut app: impl App + 'static) -
|
|||
|
||||
let frame_time = (Instant::now() - egui_start).as_secs_f64() as f32;
|
||||
previous_frame_time = Some(frame_time);
|
||||
painter.paint_jobs(&display, paint_jobs, &ctx.texture());
|
||||
painter.paint_jobs(&display, ctx.pixels_per_point(), paint_jobs, &ctx.texture());
|
||||
|
||||
{
|
||||
let egui::app::AppOutput { quit } = app_output;
|
||||
let egui::app::AppOutput {
|
||||
quit,
|
||||
pixels_per_point,
|
||||
} = app_output;
|
||||
if let Some(pixels_per_point) = pixels_per_point {
|
||||
// User changed GUI scale
|
||||
raw_input.pixels_per_point = Some(pixels_per_point);
|
||||
}
|
||||
|
||||
*control_flow = if quit {
|
||||
glutin::event_loop::ControlFlow::Exit
|
||||
|
|
|
@ -27,30 +27,16 @@ pub fn input_to_egui(
|
|||
use glutin::event::WindowEvent::*;
|
||||
match event {
|
||||
CloseRequested | Destroyed => *control_flow = ControlFlow::Exit,
|
||||
|
||||
Resized(physical_size) => {
|
||||
raw_input.screen_size =
|
||||
egui::vec2(physical_size.width as f32, physical_size.height as f32)
|
||||
/ raw_input.pixels_per_point.unwrap();
|
||||
}
|
||||
|
||||
ScaleFactorChanged {
|
||||
scale_factor,
|
||||
new_inner_size,
|
||||
} => {
|
||||
raw_input.pixels_per_point = Some(scale_factor as f32);
|
||||
raw_input.screen_size =
|
||||
egui::vec2(new_inner_size.width as f32, new_inner_size.height as f32)
|
||||
/ (scale_factor as f32);
|
||||
}
|
||||
|
||||
MouseInput { state, .. } => {
|
||||
raw_input.mouse_down = state == glutin::event::ElementState::Pressed;
|
||||
}
|
||||
CursorMoved { position, .. } => {
|
||||
CursorMoved {
|
||||
position: pos_in_pixels,
|
||||
..
|
||||
} => {
|
||||
raw_input.mouse_pos = Some(pos2(
|
||||
position.x as f32 / raw_input.pixels_per_point.unwrap(),
|
||||
position.y as f32 / raw_input.pixels_per_point.unwrap(),
|
||||
pos_in_pixels.x as f32 / raw_input.pixels_per_point.unwrap(),
|
||||
pos_in_pixels.y as f32 / raw_input.pixels_per_point.unwrap(),
|
||||
));
|
||||
}
|
||||
CursorLeft { .. } => {
|
||||
|
@ -213,14 +199,11 @@ pub fn seconds_since_midnight() -> f64 {
|
|||
time.num_seconds_from_midnight() as f64 + 1e-9 * (time.nanosecond() as f64)
|
||||
}
|
||||
|
||||
pub fn make_raw_input(display: &glium::Display) -> egui::RawInput {
|
||||
let pixels_per_point = display.gl_window().window().scale_factor() as f32;
|
||||
egui::RawInput {
|
||||
screen_size: {
|
||||
let (width, height) = display.get_framebuffer_dimensions();
|
||||
vec2(width as f32, height as f32) / pixels_per_point
|
||||
},
|
||||
pixels_per_point: Some(pixels_per_point),
|
||||
..Default::default()
|
||||
}
|
||||
pub fn screen_size_in_pixels(display: &glium::Display) -> Vec2 {
|
||||
let (width_in_pixels, height_in_pixels) = display.get_framebuffer_dimensions();
|
||||
vec2(width_in_pixels as f32, height_in_pixels as f32)
|
||||
}
|
||||
|
||||
pub fn native_pixels_per_point(display: &glium::Display) -> f32 {
|
||||
display.gl_window().window().scale_factor() as f32
|
||||
}
|
||||
|
|
|
@ -152,16 +152,23 @@ impl Painter {
|
|||
pub fn paint_jobs(
|
||||
&mut self,
|
||||
display: &glium::Display,
|
||||
pixels_per_point: f32,
|
||||
jobs: PaintJobs,
|
||||
texture: &egui::Texture,
|
||||
egui_texture: &egui::Texture,
|
||||
) {
|
||||
self.upload_egui_texture(display, texture);
|
||||
self.upload_egui_texture(display, egui_texture);
|
||||
self.upload_pending_user_textures(display);
|
||||
|
||||
let mut target = display.draw();
|
||||
target.clear_color(0.0, 0.0, 0.0, 0.0);
|
||||
for (clip_rect, triangles) in jobs {
|
||||
self.paint_job(&mut target, display, clip_rect, &triangles)
|
||||
self.paint_job(
|
||||
&mut target,
|
||||
display,
|
||||
pixels_per_point,
|
||||
clip_rect,
|
||||
&triangles,
|
||||
)
|
||||
}
|
||||
target.finish().unwrap();
|
||||
}
|
||||
|
@ -183,6 +190,7 @@ impl Painter {
|
|||
&mut self,
|
||||
target: &mut Frame,
|
||||
display: &glium::Display,
|
||||
pixels_per_point: f32,
|
||||
clip_rect: Rect,
|
||||
triangles: &Triangles,
|
||||
) {
|
||||
|
@ -217,15 +225,14 @@ impl Painter {
|
|||
let index_buffer =
|
||||
glium::IndexBuffer::new(display, PrimitiveType::TrianglesList, &indices).unwrap();
|
||||
|
||||
let pixels_per_point = display.gl_window().window().scale_factor() as f32;
|
||||
let (width_pixels, height_pixels) = display.get_framebuffer_dimensions();
|
||||
let width_points = width_pixels as f32 / pixels_per_point;
|
||||
let height_points = height_pixels as f32 / pixels_per_point;
|
||||
let (width_in_pixels, height_in_pixels) = display.get_framebuffer_dimensions();
|
||||
let width_in_points = width_in_pixels as f32 / pixels_per_point;
|
||||
let height_in_points = height_in_pixels as f32 / pixels_per_point;
|
||||
|
||||
let texture = self.get_texture(triangles.texture_id);
|
||||
|
||||
let uniforms = uniform! {
|
||||
u_screen_size: [width_points, height_points],
|
||||
u_screen_size: [width_in_points, height_in_points],
|
||||
u_sampler: texture.sampled().wrap_function(SamplerWrapFunction::Clamp),
|
||||
};
|
||||
|
||||
|
@ -255,10 +262,10 @@ impl Painter {
|
|||
let clip_max_y = pixels_per_point * clip_rect.max.y;
|
||||
|
||||
// Make sure clip rect can fit withing an `u32`:
|
||||
let clip_min_x = clamp(clip_min_x, 0.0..=width_pixels as f32);
|
||||
let clip_min_y = clamp(clip_min_y, 0.0..=height_pixels as f32);
|
||||
let clip_max_x = clamp(clip_max_x, clip_min_x..=width_pixels as f32);
|
||||
let clip_max_y = clamp(clip_max_y, clip_min_y..=height_pixels as f32);
|
||||
let clip_min_x = clamp(clip_min_x, 0.0..=width_in_pixels as f32);
|
||||
let clip_min_y = clamp(clip_min_y, 0.0..=height_in_pixels as f32);
|
||||
let clip_max_x = clamp(clip_max_x, clip_min_x..=width_in_pixels as f32);
|
||||
let clip_max_y = clamp(clip_max_y, clip_min_y..=height_in_pixels as f32);
|
||||
|
||||
let clip_min_x = clip_min_x.round() as u32;
|
||||
let clip_min_y = clip_min_y.round() as u32;
|
||||
|
@ -269,7 +276,7 @@ impl Painter {
|
|||
blend,
|
||||
scissor: Some(glium::Rect {
|
||||
left: clip_min_x,
|
||||
bottom: height_pixels - clip_max_y,
|
||||
bottom: height_in_pixels - clip_max_y,
|
||||
width: clip_max_x - clip_min_x,
|
||||
height: clip_max_y - clip_min_y,
|
||||
}),
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::*;
|
|||
|
||||
pub use egui::{
|
||||
app::{App, WebInfo},
|
||||
Srgba,
|
||||
pos2, Srgba,
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -105,13 +105,17 @@ pub struct WebInput {
|
|||
}
|
||||
|
||||
impl WebInput {
|
||||
pub fn new_frame(&mut self) -> egui::RawInput {
|
||||
pub fn new_frame(&mut self, pixels_per_point: f32) -> egui::RawInput {
|
||||
// Compensate for potential different scale of Egui compared to native.
|
||||
let scale = native_pixels_per_point() / pixels_per_point;
|
||||
let scroll_delta = std::mem::take(&mut self.scroll_delta) * scale;
|
||||
let mouse_pos = self.mouse_pos.map(|mp| pos2(mp.x * scale, mp.y * scale));
|
||||
egui::RawInput {
|
||||
mouse_down: self.mouse_down,
|
||||
mouse_pos: self.mouse_pos,
|
||||
scroll_delta: std::mem::take(&mut self.scroll_delta),
|
||||
screen_size: screen_size().unwrap(),
|
||||
pixels_per_point: Some(pixels_per_point()),
|
||||
mouse_pos,
|
||||
scroll_delta,
|
||||
screen_size: screen_size_in_native_points().unwrap() * scale,
|
||||
pixels_per_point: Some(pixels_per_point),
|
||||
time: now_sec(),
|
||||
events: std::mem::take(&mut self.events),
|
||||
}
|
||||
|
@ -121,6 +125,7 @@ impl WebInput {
|
|||
// ----------------------------------------------------------------------------
|
||||
|
||||
pub struct AppRunner {
|
||||
pixels_per_point: f32,
|
||||
pub web_backend: WebBackend,
|
||||
pub web_input: WebInput,
|
||||
pub app: Box<dyn App>,
|
||||
|
@ -130,6 +135,7 @@ pub struct AppRunner {
|
|||
impl AppRunner {
|
||||
pub fn new(web_backend: WebBackend, app: Box<dyn App>) -> Result<Self, JsValue> {
|
||||
Ok(Self {
|
||||
pixels_per_point: native_pixels_per_point(),
|
||||
web_backend,
|
||||
web_input: Default::default(),
|
||||
app,
|
||||
|
@ -142,9 +148,9 @@ impl AppRunner {
|
|||
}
|
||||
|
||||
pub fn logic(&mut self) -> Result<(egui::Output, egui::PaintJobs), JsValue> {
|
||||
resize_to_screen_size(self.web_backend.canvas_id());
|
||||
resize_canvas_to_screen_size(self.web_backend.canvas_id());
|
||||
|
||||
let raw_input = self.web_input.new_frame();
|
||||
let raw_input = self.web_input.new_frame(self.pixels_per_point);
|
||||
|
||||
let backend_info = egui::app::BackendInfo {
|
||||
web_info: Some(WebInfo {
|
||||
|
@ -152,6 +158,7 @@ impl AppRunner {
|
|||
}),
|
||||
cpu_usage: self.web_backend.previous_frame_time,
|
||||
seconds_since_midnight: Some(seconds_since_midnight()),
|
||||
native_pixels_per_point: Some(native_pixels_per_point()),
|
||||
};
|
||||
|
||||
let mut ui = self.web_backend.begin_frame(raw_input);
|
||||
|
@ -162,7 +169,14 @@ impl AppRunner {
|
|||
handle_output(&egui_output);
|
||||
|
||||
{
|
||||
let egui::app::AppOutput { quit: _ } = app_output;
|
||||
let egui::app::AppOutput {
|
||||
quit: _,
|
||||
pixels_per_point,
|
||||
} = app_output;
|
||||
|
||||
if let Some(pixels_per_point) = pixels_per_point {
|
||||
self.pixels_per_point = pixels_per_point;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((egui_output, paint_jobs))
|
||||
|
|
|
@ -17,14 +17,6 @@ pub fn console_log(s: String) {
|
|||
web_sys::console::log_1(&s.into());
|
||||
}
|
||||
|
||||
pub fn screen_size() -> Option<egui::Vec2> {
|
||||
let window = web_sys::window()?;
|
||||
Some(egui::Vec2::new(
|
||||
window.inner_width().ok()?.as_f64()? as f32,
|
||||
window.inner_height().ok()?.as_f64()? as f32,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn now_sec() -> f64 {
|
||||
web_sys::window()
|
||||
.expect("should have a Window")
|
||||
|
@ -40,7 +32,15 @@ pub fn seconds_since_midnight() -> f64 {
|
|||
seconds as f64 + 1e-3 * (d.get_milliseconds() as f64)
|
||||
}
|
||||
|
||||
pub fn pixels_per_point() -> f32 {
|
||||
pub fn screen_size_in_native_points() -> Option<egui::Vec2> {
|
||||
let window = web_sys::window()?;
|
||||
Some(egui::Vec2::new(
|
||||
window.inner_width().ok()?.as_f64()? as f32,
|
||||
window.inner_height().ok()?.as_f64()? as f32,
|
||||
))
|
||||
}
|
||||
|
||||
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() {
|
||||
pixels_per_point
|
||||
|
@ -78,11 +78,11 @@ pub fn pos_from_touch_event(event: &web_sys::TouchEvent) -> egui::Pos2 {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn resize_to_screen_size(canvas_id: &str) -> Option<()> {
|
||||
pub fn resize_canvas_to_screen_size(canvas_id: &str) -> Option<()> {
|
||||
let canvas = canvas_element(canvas_id)?;
|
||||
|
||||
let screen_size = screen_size()?;
|
||||
let pixels_per_point = pixels_per_point();
|
||||
let screen_size = screen_size_in_native_points()?;
|
||||
let pixels_per_point = native_pixels_per_point();
|
||||
canvas
|
||||
.style()
|
||||
.set_property("width", &format!("{}px", screen_size.x))
|
||||
|
|
Loading…
Reference in a new issue