mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 22:30:03 -04:00
eframe: Replace Frame::update with fn logic and fn ui (#7775)
* Part of https://github.com/emilk/egui/issues/5113 * Part of https://github.com/emilk/egui/issues/3524 ## What This deprecates `eframe::App::update` and replaces it with two new functions: ```rs pub trait App { /// Called just before `ui`, and in the future this will /// also be called for background apps when needed. fn logic(&mut self, ctx: &egui::Context, frame: &mut Frame) { } /// Show your user interface to the user. fn ui(&mut self, ui: &mut egui::Ui, frame: &mut Frame); … } ``` Similarly, `Context::run` is deprecated in favor of `Context::run_ui`. `Plugin`s are now handed a `Ui` instead of just a `Context` in `on_begin/end_frame`. ## TODO …either in this PR or a later one * [x] Deprecate `App::update` * [x] Deprecate `Context::run` * [x] Change plugins to get a `Ui` * [x] Update kittest * [x] Change viewports to get UI:s (`show_viewport_immediate` etc) - https://github.com/emilk/egui/pull/7779 ## Later PRs * [ ] Deprecate `Panel::show` * [ ] Deprecate `CentralPanel::show` * [ ] Deprecate `CentralPanel` ?
This commit is contained in:
@@ -291,13 +291,13 @@ impl<'a> PanelSizer<'a> {
|
||||
/// See the [module level docs](crate::containers::panel) for more details.
|
||||
///
|
||||
/// ```
|
||||
/// # egui::__run_test_ctx(|ctx| {
|
||||
/// egui::Panel::left("my_left_panel").show(ctx, |ui| {
|
||||
/// # egui::__run_test_ui(|ui| {
|
||||
/// egui::Panel::left("my_left_panel").show_inside(ui, |ui| {
|
||||
/// ui.label("Hello World!");
|
||||
/// });
|
||||
/// # });
|
||||
/// ```
|
||||
#[must_use = "You should call .show()"]
|
||||
#[must_use = "You should call .show_inside()"]
|
||||
pub struct Panel {
|
||||
side: PanelSide,
|
||||
id: Id,
|
||||
@@ -963,16 +963,16 @@ impl Panel {
|
||||
/// See the [module level docs](crate::containers::panel) for more details.
|
||||
///
|
||||
/// ```
|
||||
/// # egui::__run_test_ctx(|ctx| {
|
||||
/// egui::Panel::top("my_panel").show(ctx, |ui| {
|
||||
/// # egui::__run_test_ui(|ui| {
|
||||
/// egui::Panel::top("my_panel").show_inside(ui, |ui| {
|
||||
/// ui.label("Hello World! From `Panel`, that must be before `CentralPanel`!");
|
||||
/// });
|
||||
/// egui::CentralPanel::default().show(ctx, |ui| {
|
||||
/// egui::CentralPanel::default().show_inside(ui, |ui| {
|
||||
/// ui.label("Hello World!");
|
||||
/// });
|
||||
/// # });
|
||||
/// ```
|
||||
#[must_use = "You should call .show()"]
|
||||
#[must_use = "You should call .show_inside()"]
|
||||
#[derive(Default)]
|
||||
pub struct CentralPanel {
|
||||
frame: Option<Frame>,
|
||||
|
||||
@@ -790,9 +790,13 @@ impl Context {
|
||||
|
||||
#[must_use]
|
||||
fn run_ui_dyn(&self, new_input: RawInput, run_ui: &mut dyn FnMut(&mut Ui)) -> FullOutput {
|
||||
let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
|
||||
#[expect(deprecated)]
|
||||
self.run(new_input, |ctx| {
|
||||
crate::CentralPanel::no_frame().show(ctx, |ui| {
|
||||
plugins.on_begin_pass(ui);
|
||||
run_ui(ui);
|
||||
plugins.on_end_pass(ui);
|
||||
});
|
||||
})
|
||||
}
|
||||
@@ -824,6 +828,7 @@ impl Context {
|
||||
/// ## See also
|
||||
/// * [`Self::run_ui`]
|
||||
#[must_use]
|
||||
#[deprecated = "Call run_ui instead"]
|
||||
pub fn run(&self, new_input: RawInput, mut run_ui: impl FnMut(&Self)) -> FullOutput {
|
||||
self.run_dyn(new_input, &mut run_ui)
|
||||
}
|
||||
@@ -924,9 +929,6 @@ impl Context {
|
||||
plugins.on_input(&mut new_input);
|
||||
|
||||
self.write(|ctx| ctx.begin_pass(new_input));
|
||||
|
||||
// Plugins run just after the pass starts:
|
||||
plugins.on_begin_pass(self);
|
||||
}
|
||||
|
||||
/// See [`Self::begin_pass`].
|
||||
@@ -2378,15 +2380,14 @@ impl Context {
|
||||
crate::gui_zoom::zoom_with_keyboard(self);
|
||||
}
|
||||
|
||||
// Plugins run just before the pass ends.
|
||||
let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
|
||||
plugins.on_end_pass(self);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
self.debug_painting();
|
||||
|
||||
let mut output = self.write(|ctx| ctx.end_pass());
|
||||
|
||||
let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
|
||||
plugins.on_output(&mut output);
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
@@ -4039,10 +4040,8 @@ impl Context {
|
||||
viewport.class = ViewportClass::Deferred;
|
||||
viewport.builder = viewport_builder;
|
||||
viewport.used = true;
|
||||
viewport.viewport_ui_cb = Some(Arc::new(move |ctx| {
|
||||
crate::CentralPanel::no_frame().show(ctx, |ui| {
|
||||
(viewport_ui_cb)(ui, ViewportClass::Deferred);
|
||||
});
|
||||
viewport.viewport_ui_cb = Some(Arc::new(move |ui| {
|
||||
(viewport_ui_cb)(ui, ViewportClass::Deferred);
|
||||
}));
|
||||
});
|
||||
}
|
||||
@@ -4118,10 +4117,8 @@ impl Context {
|
||||
let viewport = ImmediateViewport {
|
||||
ids,
|
||||
builder,
|
||||
viewport_ui_cb: Box::new(move |ctx| {
|
||||
crate::CentralPanel::no_frame().show(ctx, |ui| {
|
||||
*out = Some((viewport_ui_cb)(ui, ViewportClass::Immediate));
|
||||
});
|
||||
viewport_ui_cb: Box::new(move |ui| {
|
||||
*out = Some((viewport_ui_cb)(ui, ViewportClass::Immediate));
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -4246,11 +4243,11 @@ mod test {
|
||||
// A single call, no request to discard:
|
||||
{
|
||||
let mut num_calls = 0;
|
||||
let output = ctx.run(Default::default(), |ctx| {
|
||||
let output = ctx.run_ui(Default::default(), |ui| {
|
||||
num_calls += 1;
|
||||
assert_eq!(ctx.output(|o| o.num_completed_passes), 0);
|
||||
assert!(!ctx.output(|o| o.requested_discard()));
|
||||
assert!(!ctx.will_discard());
|
||||
assert_eq!(ui.output(|o| o.num_completed_passes), 0);
|
||||
assert!(!ui.output(|o| o.requested_discard()));
|
||||
assert!(!ui.will_discard());
|
||||
});
|
||||
assert_eq!(num_calls, 1);
|
||||
assert_eq!(output.platform_output.num_completed_passes, 1);
|
||||
@@ -4260,10 +4257,10 @@ mod test {
|
||||
// A single call, with a denied request to discard:
|
||||
{
|
||||
let mut num_calls = 0;
|
||||
let output = ctx.run(Default::default(), |ctx| {
|
||||
let output = ctx.run_ui(Default::default(), |ui| {
|
||||
num_calls += 1;
|
||||
ctx.request_discard("test");
|
||||
assert!(!ctx.will_discard(), "The request should have been denied");
|
||||
ui.request_discard("test");
|
||||
assert!(!ui.will_discard(), "The request should have been denied");
|
||||
});
|
||||
assert_eq!(num_calls, 1);
|
||||
assert_eq!(output.platform_output.num_completed_passes, 1);
|
||||
@@ -4291,10 +4288,10 @@ mod test {
|
||||
// Normal single pass:
|
||||
{
|
||||
let mut num_calls = 0;
|
||||
let output = ctx.run(Default::default(), |ctx| {
|
||||
assert_eq!(ctx.output(|o| o.num_completed_passes), 0);
|
||||
assert!(!ctx.output(|o| o.requested_discard()));
|
||||
assert!(!ctx.will_discard());
|
||||
let output = ctx.run_ui(Default::default(), |ui| {
|
||||
assert_eq!(ui.output(|o| o.num_completed_passes), 0);
|
||||
assert!(!ui.output(|o| o.requested_discard()));
|
||||
assert!(!ui.will_discard());
|
||||
num_calls += 1;
|
||||
});
|
||||
assert_eq!(num_calls, 1);
|
||||
@@ -4305,13 +4302,13 @@ mod test {
|
||||
// Request discard once:
|
||||
{
|
||||
let mut num_calls = 0;
|
||||
let output = ctx.run(Default::default(), |ctx| {
|
||||
assert_eq!(ctx.output(|o| o.num_completed_passes), num_calls);
|
||||
let output = ctx.run_ui(Default::default(), |ui| {
|
||||
assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);
|
||||
|
||||
assert!(!ctx.will_discard());
|
||||
assert!(!ui.will_discard());
|
||||
if num_calls == 0 {
|
||||
ctx.request_discard("test");
|
||||
assert!(ctx.will_discard());
|
||||
ui.request_discard("test");
|
||||
assert!(ui.will_discard());
|
||||
}
|
||||
|
||||
num_calls += 1;
|
||||
@@ -4327,15 +4324,15 @@ mod test {
|
||||
// Request discard twice:
|
||||
{
|
||||
let mut num_calls = 0;
|
||||
let output = ctx.run(Default::default(), |ctx| {
|
||||
assert_eq!(ctx.output(|o| o.num_completed_passes), num_calls);
|
||||
let output = ctx.run_ui(Default::default(), |ui| {
|
||||
assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);
|
||||
|
||||
assert!(!ctx.will_discard());
|
||||
ctx.request_discard("test");
|
||||
assert!(!ui.will_discard());
|
||||
ui.request_discard("test");
|
||||
if num_calls == 0 {
|
||||
assert!(ctx.will_discard(), "First request granted");
|
||||
assert!(ui.will_discard(), "First request granted");
|
||||
} else {
|
||||
assert!(!ctx.will_discard(), "Second request should be denied");
|
||||
assert!(!ui.will_discard(), "Second request should be denied");
|
||||
}
|
||||
|
||||
num_calls += 1;
|
||||
@@ -4357,13 +4354,13 @@ mod test {
|
||||
// Request discard three times:
|
||||
{
|
||||
let mut num_calls = 0;
|
||||
let output = ctx.run(Default::default(), |ctx| {
|
||||
assert_eq!(ctx.output(|o| o.num_completed_passes), num_calls);
|
||||
let output = ctx.run_ui(Default::default(), |ui| {
|
||||
assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);
|
||||
|
||||
assert!(!ctx.will_discard());
|
||||
assert!(!ui.will_discard());
|
||||
if num_calls <= 2 {
|
||||
ctx.request_discard("test");
|
||||
assert!(ctx.will_discard());
|
||||
ui.request_discard("test");
|
||||
assert!(ui.will_discard());
|
||||
}
|
||||
|
||||
num_calls += 1;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//! to get callbacks on certain events ([`Plugin::on_begin_pass`], [`Plugin::on_end_pass`]).
|
||||
|
||||
use crate::{
|
||||
Align, Align2, Color32, Context, FontFamily, FontId, Plugin, Rect, Shape, Vec2, WidgetText,
|
||||
Align, Align2, Color32, Context, FontFamily, FontId, Plugin, Rect, Shape, Ui, Vec2, WidgetText,
|
||||
text,
|
||||
};
|
||||
|
||||
@@ -57,9 +57,9 @@ impl Plugin for DebugTextPlugin {
|
||||
"DebugTextPlugin"
|
||||
}
|
||||
|
||||
fn on_end_pass(&mut self, ctx: &Context) {
|
||||
fn on_end_pass(&mut self, ui: &mut Ui) {
|
||||
let entries = std::mem::take(&mut self.entries);
|
||||
Self::paint_entries(ctx, entries);
|
||||
Self::paint_entries(ui, entries);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{any::Any, sync::Arc};
|
||||
|
||||
use crate::{Context, CursorIcon, Plugin};
|
||||
use crate::{Context, CursorIcon, Plugin, Ui};
|
||||
|
||||
/// Plugin for tracking drag-and-drop payload.
|
||||
///
|
||||
@@ -32,12 +32,12 @@ impl Plugin for DragAndDrop {
|
||||
/// Interrupt drag-and-drop if the user presses the escape key.
|
||||
///
|
||||
/// This needs to happen at frame start so we can properly capture the escape key.
|
||||
fn on_begin_pass(&mut self, ctx: &Context) {
|
||||
fn on_begin_pass(&mut self, ui: &mut Ui) {
|
||||
let has_any_payload = self.payload.is_some();
|
||||
|
||||
if has_any_payload {
|
||||
let abort_dnd_due_to_escape_key =
|
||||
ctx.input_mut(|i| i.consume_key(crate::Modifiers::NONE, crate::Key::Escape));
|
||||
ui.input_mut(|i| i.consume_key(crate::Modifiers::NONE, crate::Key::Escape));
|
||||
|
||||
if abort_dnd_due_to_escape_key {
|
||||
self.payload = None;
|
||||
@@ -50,18 +50,18 @@ impl Plugin for DragAndDrop {
|
||||
/// This is a catch-all safety net in case user code doesn't capture the drag payload itself.
|
||||
/// This must happen at end-of-frame such that we don't shadow the mouse release event from user
|
||||
/// code.
|
||||
fn on_end_pass(&mut self, ctx: &Context) {
|
||||
fn on_end_pass(&mut self, ui: &mut Ui) {
|
||||
let has_any_payload = self.payload.is_some();
|
||||
|
||||
if has_any_payload {
|
||||
let abort_dnd_due_to_mouse_release = ctx.input_mut(|i| i.pointer.any_released());
|
||||
let abort_dnd_due_to_mouse_release = ui.input_mut(|i| i.pointer.any_released());
|
||||
|
||||
if abort_dnd_due_to_mouse_release {
|
||||
self.payload = None;
|
||||
} else {
|
||||
// We set the cursor icon only if its default, as the user code might have
|
||||
// explicitly set it already.
|
||||
ctx.output_mut(|o| {
|
||||
ui.output_mut(|o| {
|
||||
if o.cursor_icon == CursorIcon::Default {
|
||||
o.cursor_icon = CursorIcon::Grabbing;
|
||||
}
|
||||
|
||||
@@ -43,23 +43,6 @@
|
||||
//! In some GUI frameworks this would require defining multiple types and functions with callbacks or message handlers,
|
||||
//! but thanks to `egui` being immediate mode everything is one self-contained function!
|
||||
//!
|
||||
//! ### Getting a [`Ui`]
|
||||
//!
|
||||
//! Use one of [`Panel`], [`CentralPanel`], [`Window`] or [`Area`] to
|
||||
//! get access to an [`Ui`] where you can put widgets. For example:
|
||||
//!
|
||||
//! ```
|
||||
//! # egui::__run_test_ctx(|ctx| {
|
||||
//! egui::CentralPanel::default().show(&ctx, |ui| {
|
||||
//! ui.add(egui::Label::new("Hello World!"));
|
||||
//! ui.label("A shorter and more convenient way to add a label.");
|
||||
//! if ui.button("Click me").clicked() {
|
||||
//! // take some action here
|
||||
//! }
|
||||
//! });
|
||||
//! # });
|
||||
//! ```
|
||||
//!
|
||||
//! ### Quick start
|
||||
//!
|
||||
//! ```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{Context, FullOutput, RawInput};
|
||||
use crate::{Context, FullOutput, RawInput, Ui};
|
||||
use ahash::HashMap;
|
||||
use epaint::mutex::{Mutex, MutexGuard};
|
||||
use std::sync::Arc;
|
||||
@@ -23,13 +23,13 @@ pub trait Plugin: Send + Sync + std::any::Any + 'static {
|
||||
|
||||
/// Called at the start of each pass.
|
||||
///
|
||||
/// Can be used to show ui, e.g. a [`crate::Window`] or [`crate::SidePanel`].
|
||||
fn on_begin_pass(&mut self, ctx: &Context) {}
|
||||
/// Can be used to show ui, e.g. a [`crate::Window`] or [`crate::Panel`].
|
||||
fn on_begin_pass(&mut self, ui: &mut Ui) {}
|
||||
|
||||
/// Called at the end of each pass.
|
||||
///
|
||||
/// Can be used to show ui, e.g. a [`crate::Window`].
|
||||
fn on_end_pass(&mut self, ctx: &Context) {}
|
||||
fn on_end_pass(&mut self, ui: &mut Ui) {}
|
||||
|
||||
/// Called just before the input is processed.
|
||||
///
|
||||
@@ -147,17 +147,17 @@ impl PluginsOrdered {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_begin_pass(&self, ctx: &Context) {
|
||||
pub fn on_begin_pass(&self, ui: &mut Ui) {
|
||||
profiling::scope!("plugins", "on_begin_pass");
|
||||
self.for_each_dyn(|p| {
|
||||
p.on_begin_pass(ctx);
|
||||
p.on_begin_pass(ui);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn on_end_pass(&self, ctx: &Context) {
|
||||
pub fn on_end_pass(&self, ui: &mut Ui) {
|
||||
profiling::scope!("plugins", "on_end_pass");
|
||||
self.for_each_dyn(|p| {
|
||||
p.on_end_pass(ctx);
|
||||
p.on_end_pass(ui);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ impl Plugins {
|
||||
}
|
||||
|
||||
/// Generic event callback.
|
||||
pub type ContextCallback = Arc<dyn Fn(&Context) + Send + Sync>;
|
||||
pub type ContextCallback = Arc<dyn Fn(&mut Ui) + Send + Sync>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct CallbackPlugin {
|
||||
@@ -227,21 +227,21 @@ impl Plugin for CallbackPlugin {
|
||||
"CallbackPlugins"
|
||||
}
|
||||
|
||||
fn on_begin_pass(&mut self, ctx: &Context) {
|
||||
fn on_begin_pass(&mut self, ui: &mut Ui) {
|
||||
profiling::function_scope!();
|
||||
|
||||
for (_debug_name, cb) in &self.on_begin_plugins {
|
||||
profiling::scope!("on_begin_pass", *_debug_name);
|
||||
(cb)(ctx);
|
||||
(cb)(ui);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_end_pass(&mut self, ctx: &Context) {
|
||||
fn on_end_pass(&mut self, ui: &mut Ui) {
|
||||
profiling::function_scope!();
|
||||
|
||||
for (_debug_name, cb) in &self.on_end_plugins {
|
||||
profiling::scope!("on_end_pass", *_debug_name);
|
||||
(cb)(ctx);
|
||||
(cb)(ui);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,8 +128,8 @@ impl Plugin for LabelSelectionState {
|
||||
"LabelSelectionState"
|
||||
}
|
||||
|
||||
fn on_begin_pass(&mut self, ctx: &Context) {
|
||||
if ctx.input(|i| i.pointer.any_pressed() && !i.modifiers.shift) {
|
||||
fn on_begin_pass(&mut self, ui: &mut Ui) {
|
||||
if ui.input(|i| i.pointer.any_pressed() && !i.modifiers.shift) {
|
||||
// Maybe a new selection is about to begin, but the old one is over:
|
||||
// state.selection = None; // TODO(emilk): this makes sense, but doesn't work as expected.
|
||||
}
|
||||
@@ -145,9 +145,9 @@ impl Plugin for LabelSelectionState {
|
||||
self.painted_selections.clear();
|
||||
}
|
||||
|
||||
fn on_end_pass(&mut self, ctx: &Context) {
|
||||
fn on_end_pass(&mut self, ui: &mut Ui) {
|
||||
if self.is_dragging {
|
||||
ctx.set_cursor_icon(CursorIcon::Text);
|
||||
ui.set_cursor_icon(CursorIcon::Text);
|
||||
}
|
||||
|
||||
if !self.has_reached_primary || !self.has_reached_secondary {
|
||||
@@ -159,7 +159,7 @@ impl Plugin for LabelSelectionState {
|
||||
if let Some(selection) = prev_selection {
|
||||
// This was the first frame of glitch, so hide the
|
||||
// glitching by removing all painted selections:
|
||||
ctx.graphics_mut(|layers| {
|
||||
ui.graphics_mut(|layers| {
|
||||
if let Some(list) = layers.get_mut(selection.layer_id) {
|
||||
for (shape_idx, row_selections) in self.painted_selections.drain(..) {
|
||||
list.mutate_shape(shape_idx, |shape| {
|
||||
@@ -190,21 +190,21 @@ impl Plugin for LabelSelectionState {
|
||||
}
|
||||
}
|
||||
|
||||
let pressed_escape = ctx.input(|i| i.key_pressed(crate::Key::Escape));
|
||||
let clicked_something_else = ctx.input(|i| i.pointer.any_pressed()) && !self.any_hovered;
|
||||
let pressed_escape = ui.input(|i| i.key_pressed(crate::Key::Escape));
|
||||
let clicked_something_else = ui.input(|i| i.pointer.any_pressed()) && !self.any_hovered;
|
||||
let delected_everything = pressed_escape || clicked_something_else;
|
||||
|
||||
if delected_everything {
|
||||
self.selection = None;
|
||||
}
|
||||
|
||||
if ctx.input(|i| i.pointer.any_released()) {
|
||||
if ui.input(|i| i.pointer.any_released()) {
|
||||
self.is_dragging = false;
|
||||
}
|
||||
|
||||
let text_to_copy = std::mem::take(&mut self.text_to_copy);
|
||||
if !text_to_copy.is_empty() {
|
||||
ctx.copy_text(text_to_copy);
|
||||
ui.copy_text(text_to_copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ use std::sync::Arc;
|
||||
|
||||
use epaint::{Pos2, Vec2};
|
||||
|
||||
use crate::{Context, Id};
|
||||
use crate::{Context, Id, Ui};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -263,7 +263,7 @@ impl ViewportIdPair {
|
||||
}
|
||||
|
||||
/// The user-code that shows the ui in the viewport, used for deferred viewports.
|
||||
pub type DeferredViewportUiCallback = dyn Fn(&Context) + Sync + Send;
|
||||
pub type DeferredViewportUiCallback = dyn Fn(&mut Ui) + Sync + Send;
|
||||
|
||||
/// Render the given viewport, calling the given ui callback.
|
||||
pub type ImmediateViewportRendererCallback = dyn for<'a> Fn(&Context, ImmediateViewport<'a>);
|
||||
@@ -1251,5 +1251,5 @@ pub struct ImmediateViewport<'a> {
|
||||
pub builder: ViewportBuilder,
|
||||
|
||||
/// The user-code that shows the GUI.
|
||||
pub viewport_ui_cb: Box<dyn FnMut(&Context) + 'a>,
|
||||
pub viewport_ui_cb: Box<dyn FnMut(&mut Ui) + 'a>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user