`Context::begin_frame()` no longer returns anything.

Put your widgets into a `SidePanel`, `TopPanel`,
`Window` or into `ctx.background_ui()`.
This commit is contained in:
Emil Ernerfeldt 2020-10-24 10:56:23 +02:00
parent f86cb4a923
commit 44a7cac046
11 changed files with 54 additions and 37 deletions

View File

@ -10,6 +10,8 @@
* Panels: you can now create panels using `SidePanel` and `TopPanel`. * Panels: you can now create panels using `SidePanel` and `TopPanel`.
* Fix a bug where some regions would slowly grow for non-integral scales (`pixels_per_point`). * Fix a bug where some regions would slowly grow for non-integral scales (`pixels_per_point`).
* You can no longer throw windows * You can no longer throw windows
* `Context::begin_frame()` no longer returns anything.
* Put your widgets into a `SidePanel`, `TopPanel`, `Window` or into `ctx.background_ui()`.
## 0.2.0 - 2020-10-10 ## 0.2.0 - 2020-10-10

View File

@ -147,8 +147,8 @@ let mut egui_ctx = egui::Context::new();
// Game loop: // Game loop:
loop { loop {
let raw_input: egui::RawInput = my_integration.gather_input(); let raw_input: egui::RawInput = my_integration.gather_input();
let mut ui = egui_ctx.begin_frame(raw_input); egui_ctx.begin_frame(raw_input);
my_app.ui(&mut ui); // add windows and widgets to `ui` here my_app.ui(&mut egui_ctx); // add panels, windows and widgets to `egui_ctx` here
let (output, paint_jobs) = egui_ctx.end_frame(); let (output, paint_jobs) = egui_ctx.end_frame();
my_integration.paint(paint_jobs); my_integration.paint(paint_jobs);
my_integration.set_cursor_icon(output.cursor_icon); my_integration.set_cursor_icon(output.cursor_icon);

View File

@ -12,8 +12,8 @@ pub fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("demo_windows_minimal", |b| { c.bench_function("demo_windows_minimal", |b| {
b.iter(|| { b.iter(|| {
let mut ui = ctx.begin_frame(raw_input.clone()); ctx.begin_frame(raw_input.clone());
demo_windows.ui(&mut ui, &Default::default(), None); demo_windows.ui(&ctx, &Default::default(), None);
ctx.end_frame() ctx.end_frame()
}) })
}); });
@ -26,8 +26,8 @@ pub fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("demo_windows_full", |b| { c.bench_function("demo_windows_full", |b| {
b.iter(|| { b.iter(|| {
let mut ui = ctx.begin_frame(raw_input.clone()); ctx.begin_frame(raw_input.clone());
demo_windows.ui(&mut ui, &Default::default(), None); demo_windows.ui(&ctx, &Default::default(), None);
ctx.end_frame() ctx.end_frame()
}) })
}); });
@ -35,7 +35,8 @@ pub fn criterion_benchmark(c: &mut Criterion) {
{ {
let mut ctx = egui::Context::new(); let mut ctx = egui::Context::new();
let mut ui = ctx.begin_frame(raw_input); ctx.begin_frame(raw_input);
let mut ui = ctx.background_ui();
c.bench_function("label", |b| { c.bench_function("label", |b| {
b.iter(|| { b.iter(|| {
ui.label(egui::demos::LOREM_IPSUM_LONG); ui.label(egui::demos::LOREM_IPSUM_LONG);

View File

@ -7,15 +7,16 @@
// TODO: move egui/src/app.rs to own crate, e.g. egui_framework ? // TODO: move egui/src/app.rs to own crate, e.g. egui_framework ?
use crate::Ui; use crate::Context;
/// Implement this trait to write apps that can be compiled both natively using the [`egui_glium`](https://crates.io/crates/egui_glium) crate, /// Implement this trait to write apps that can be compiled both natively using the [`egui_glium`](https://crates.io/crates/egui_glium) crate,
/// and deployed as a web site using the [`egui_web`](https://crates.io/crates/egui_web) crate. /// and deployed as a web site using the [`egui_web`](https://crates.io/crates/egui_web) crate.
pub trait App { pub trait App {
/// Called each time the UI needs repainting, which may be many times per second. /// Called each time the UI needs repainting, which may be many times per second.
/// Put your widgets into a `SidePanel`, `TopPanel`, `Window` or into `ctx.background_ui()`.
fn ui( fn ui(
&mut self, &mut self,
ui: &mut Ui, ctx: &std::sync::Arc<Context>,
info: &BackendInfo, info: &BackendInfo,
tex_allocator: Option<&mut dyn TextureAllocator>, tex_allocator: Option<&mut dyn TextureAllocator>,
) -> AppOutput; ) -> AppOutput;

View File

@ -43,6 +43,7 @@ pub struct Context {
input: InputState, input: InputState,
/// Starts off as the screen_rect, shrinks as panels are added. /// Starts off as the screen_rect, shrinks as panels are added.
/// Becomes `Rect::nothing()` when `Context::background_ui` is called.
available_rect: Mutex<Option<Rect>>, available_rect: Mutex<Option<Rect>>,
// The output of a frame: // The output of a frame:
@ -198,12 +199,14 @@ impl Context {
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
/// Call at the start of every frame. /// Call at the start of every frame.
/// Returns a master fullscreen UI, covering the entire screen. /// To get a `Ui` to place widgets into you one or more of:
pub fn begin_frame(self: &mut Arc<Self>, new_input: RawInput) -> Ui { /// * `SidePanel` or `TopPanel`
/// * `Window`
/// * `Context::background_ui()`
pub fn begin_frame(self: &mut Arc<Self>, new_input: RawInput) {
let mut self_: Self = (**self).clone(); let mut self_: Self = (**self).clone();
self_.begin_frame_mut(new_input); self_.begin_frame_mut(new_input);
*self = Arc::new(self_); *self = Arc::new(self_);
self.fullscreen_ui()
} }
fn begin_frame_mut(&mut self, new_raw_input: RawInput) { fn begin_frame_mut(&mut self, new_raw_input: RawInput) {
@ -275,9 +278,20 @@ impl Context {
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
/// A `Ui` for the entire screen, behind any windows. /// A `Ui` that covers the whole background (not used by panels).
fn fullscreen_ui(self: &Arc<Self>) -> Ui { ///
let rect = self.input.screen_rect(); /// This is the same area that `Window`s are put in to,
/// so either put your UI into `background_ui()` or into `Window`s.
///
/// Call this at most once per frame.
pub fn background_ui(self: &Arc<Self>) -> Ui {
let rect = self.available_rect();
debug_assert!(
rect != Rect::nothing(),
"You already called `background_ui()` once this frame!"
);
*self.available_rect.lock() = Some(Rect::nothing()); // Nothing left after this
let layer_id = LayerId::background(); let layer_id = LayerId::background();
Ui::new(self.clone(), layer_id, layer_id.id, rect, rect) Ui::new(self.clone(), layer_id, layer_id.id, rect, rect)
} }

View File

@ -1,4 +1,5 @@
use crate::{app, demos, History, Ui}; use crate::{app, demos, Context, History, Ui};
use std::sync::Arc;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -274,7 +275,7 @@ impl DemoApp {
impl app::App for DemoApp { impl app::App for DemoApp {
fn ui( fn ui(
&mut self, &mut self,
ui: &mut Ui, ctx: &Arc<Context>,
info: &app::BackendInfo, info: &app::BackendInfo,
tex_allocator: Option<&mut dyn app::TextureAllocator>, tex_allocator: Option<&mut dyn app::TextureAllocator>,
) -> app::AppOutput { ) -> app::AppOutput {
@ -295,20 +296,20 @@ impl app::App for DemoApp {
link, link,
}; };
self.demo_windows.ui(ui, &demo_environment, tex_allocator); self.demo_windows.ui(ctx, &demo_environment, tex_allocator);
let mut output = app::AppOutput::default(); let mut output = app::AppOutput::default();
crate::Window::new("Backend") crate::Window::new("Backend")
.min_width(360.0) .min_width(360.0)
.scroll(false) .scroll(false)
.show(ui.ctx(), |ui| { .show(ctx, |ui| {
output = self.backend_ui(ui, info); output = self.backend_ui(ui, info);
}); });
if self.run_mode == RunMode::Continuous { if self.run_mode == RunMode::Continuous {
// Tell the backend to repaint as soon as possible // Tell the backend to repaint as soon as possible
ui.ctx().request_repaint(); ctx.request_repaint();
} }
output output

View File

@ -44,12 +44,10 @@ impl DemoWindows {
/// Show the app ui (menu bar and windows). /// Show the app ui (menu bar and windows).
pub fn ui( pub fn ui(
&mut self, &mut self,
ui: &mut Ui, ctx: &Arc<Context>,
env: &DemoEnvironment, env: &DemoEnvironment,
tex_allocator: Option<&mut dyn app::TextureAllocator>, tex_allocator: Option<&mut dyn app::TextureAllocator>,
) { ) {
let ctx = ui.ctx();
if self.previous_link != env.link { if self.previous_link != env.link {
match env.link { match env.link {
None => {} None => {}

View File

@ -11,8 +11,8 @@
//! // Game loop: //! // Game loop:
//! loop { //! loop {
//! let raw_input: egui::RawInput = my_integration.gather_input(); //! let raw_input: egui::RawInput = my_integration.gather_input();
//! let mut ui = egui_ctx.begin_frame(raw_input); //! egui_ctx.begin_frame(raw_input);
//! my_app.ui(&mut ui); // add windows and widgets to `ui` here //! my_app.ui(&egui_ctx); // add panels, windows and widgets to `egui_ctx` here
//! let (output, paint_jobs) = egui_ctx.end_frame(); //! let (output, paint_jobs) = egui_ctx.end_frame();
//! my_integration.paint(paint_jobs); //! my_integration.paint(paint_jobs);
//! my_integration.set_cursor_icon(output.cursor_icon); //! my_integration.set_cursor_icon(output.cursor_icon);
@ -98,8 +98,8 @@ pub fn text_egui_e2e() {
const NUM_FRAMES: usize = 5; const NUM_FRAMES: usize = 5;
for _ in 0..NUM_FRAMES { for _ in 0..NUM_FRAMES {
let mut ui = ctx.begin_frame(raw_input.clone()); ctx.begin_frame(raw_input.clone());
demo_windows.ui(&mut ui, &Default::default(), None); demo_windows.ui(&ctx, &Default::default(), None);
let (_output, paint_jobs) = ctx.end_frame(); let (_output, paint_jobs) = ctx.end_frame();
assert!(!paint_jobs.is_empty()); assert!(!paint_jobs.is_empty());
} }

View File

@ -77,8 +77,8 @@ pub fn run(
native_pixels_per_point: Some(native_pixels_per_point(&display)), native_pixels_per_point: Some(native_pixels_per_point(&display)),
}; };
let mut ui = ctx.begin_frame(raw_input.take()); ctx.begin_frame(raw_input.take());
let app_output = app.ui(&mut ui, &backend_info, Some(&mut painter)); let app_output = app.ui(&ctx, &backend_info, Some(&mut painter));
let (egui_output, paint_jobs) = ctx.end_frame(); let (egui_output, paint_jobs) = ctx.end_frame();
let frame_time = (Instant::now() - egui_start).as_secs_f64() as f32; let frame_time = (Instant::now() - egui_start).as_secs_f64() as f32;

View File

@ -33,8 +33,7 @@ impl WebBackend {
self.painter.canvas_id() self.painter.canvas_id()
} }
/// Returns a master fullscreen UI, covering the entire screen. pub fn begin_frame(&mut self, raw_input: egui::RawInput) {
pub fn begin_frame(&mut self, raw_input: egui::RawInput) -> egui::Ui {
self.frame_start = Some(now_sec()); self.frame_start = Some(now_sec());
self.ctx.begin_frame(raw_input) self.ctx.begin_frame(raw_input)
} }
@ -161,10 +160,11 @@ impl AppRunner {
native_pixels_per_point: Some(native_pixels_per_point()), native_pixels_per_point: Some(native_pixels_per_point()),
}; };
let mut ui = self.web_backend.begin_frame(raw_input); self.web_backend.begin_frame(raw_input);
let egui_ctx = &self.web_backend.ctx;
let app_output = self let app_output = self
.app .app
.ui(&mut ui, &backend_info, Some(&mut self.web_backend.painter)); .ui(egui_ctx, &backend_info, Some(&mut self.web_backend.painter));
let (egui_output, paint_jobs) = self.web_backend.end_frame()?; let (egui_output, paint_jobs) = self.web_backend.end_frame()?;
handle_output(&egui_output); handle_output(&egui_output);

View File

@ -11,18 +11,18 @@ struct MyApp {
} }
impl egui::app::App for MyApp { impl egui::app::App for MyApp {
/// This function will be called whenever the Ui needs to be shown, /// Called each time the UI needs repainting, which may be many times per second.
/// which may be many times per second. /// Put your widgets into a `SidePanel`, `TopPanel`, `Window` or into `ctx.background_ui()`.
fn ui( fn ui(
&mut self, &mut self,
ui: &mut egui::Ui, ctx: &std::sync::Arc<egui::Context>,
_info: &egui::app::BackendInfo, _info: &egui::app::BackendInfo,
_tex_allocator: Option<&mut dyn egui::app::TextureAllocator>, _tex_allocator: Option<&mut dyn egui::app::TextureAllocator>,
) -> egui::app::AppOutput { ) -> egui::app::AppOutput {
let MyApp { my_string, value } = self; let MyApp { my_string, value } = self;
// Example used in `README.md`. // Example used in `README.md`.
egui::Window::new("Debug").show(ui.ctx(), |ui| { egui::Window::new("Debug").show(ctx, |ui| {
ui.label(format!("Hello, world {}", 123)); ui.label(format!("Hello, world {}", 123));
if ui.button("Save").clicked { if ui.button("Save").clicked {
my_save_function(); my_save_function();