Replace Context::begin_frame/end_frame with fn run taking a closure (#872)

* Replace Context begin_frame/end_frame with `fn run` taking a closure
* Create `egui::__run_test_ui` to replace `Ui::__test`
* Add helper `egui::__run_test_ctx` for doctests
This commit is contained in:
Emil Ernerfeldt 2021-11-03 20:11:25 +01:00 committed by GitHub
parent e54106e950
commit 49e43885ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 294 additions and 199 deletions

View file

@ -15,6 +15,8 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w
### Changed 🔧
* Unifiy the four `Memory` data buckets (`data`, `data_temp`, `id_data` and `id_data_temp`) into a single `Memory::data`, with a new interface ([#836](https://github.com/emilk/egui/pull/836)).
* `ui.add(Button::new("…").text_color(…))` is now `ui.button(RichText::new("…").color(…))` (same for `Label` )([#855](https://github.com/emilk/egui/pull/855)).
* Replace `CtxRef::begin_frame` and `end_frame` with `CtxRef::run` ([#872](https://github.com/emilk/egui/pull/872)).
* Replace `Ui::__test` with `egui::__run_test_ui` ([#872](https://github.com/emilk/egui/pull/872)).
### Contributors 🙏
* [mankinskin](https://github.com/mankinskin) ([#543](https://github.com/emilk/egui/pull/543))

View file

@ -198,14 +198,14 @@ Missing an integration for the thing you're working on? Create one, it is easy!
You need to collect [`egui::RawInput`](https://docs.rs/egui/latest/egui/struct.RawInput.html), paint [`egui::ClippedMesh`](https://docs.rs/epaint/):es and handle [`egui::Output`](https://docs.rs/egui/latest/egui/struct.Output.html). The basic structure is this:
``` rust
let mut egui_ctx = egui::Context::new();
let mut egui_ctx = egui::CtxRef::default();
// Game loop:
loop {
let raw_input: egui::RawInput = my_integration.gather_input();
egui_ctx.begin_frame(raw_input);
my_app.ui(&mut egui_ctx); // add panels, windows and widgets to `egui_ctx` here
let (output, shapes) = egui_ctx.end_frame();
let (output, shapes) = egui_ctx.run(raw_input, |egui_ctx| {
my_app.ui(egui_ctx); // add panels, windows and widgets to `egui_ctx` here
});
let clipped_meshes = egui_ctx.tessellate(shapes); // create triangles to paint
my_integration.paint(clipped_meshes);
my_integration.set_cursor_icon(output.cursor_icon);

View file

@ -282,8 +282,6 @@ impl EpiIntegration {
let raw_input = self.egui_winit.take_egui_input(window);
self.egui_ctx.begin_frame(raw_input);
let mut app_output = epi::backend::AppOutput::default();
let mut frame = epi::backend::FrameBuilder {
info: integration_info(self.integration_name, window, self.latest_frame_time),
@ -293,9 +291,11 @@ impl EpiIntegration {
}
.build();
self.app.update(&self.egui_ctx, &mut frame);
let app = &mut self.app; // TODO: remove when we update MSVR to 1.56
let (egui_output, shapes) = self.egui_ctx.run(raw_input, |egui_ctx| {
app.update(egui_ctx, &mut frame);
});
let (egui_output, shapes) = self.egui_ctx.end_frame();
let needs_repaint = egui_output.needs_repaint;
self.egui_winit
.handle_output(window, &self.egui_ctx, egui_output);

View file

@ -33,14 +33,13 @@ impl State {
/// This forms the base of the [`Window`] container.
///
/// ```
/// # let mut ctx = egui::CtxRef::default();
/// # ctx.begin_frame(Default::default());
/// # let ctx = &ctx;
/// # egui::__run_test_ctx(|ctx| {
/// egui::Area::new("my_area")
/// .fixed_pos(egui::pos2(32.0, 32.0))
/// .show(ctx, |ui| {
/// ui.label("Floating text!");
/// });
/// # });
#[must_use = "You should call .show()"]
#[derive(Clone, Copy, Debug)]
pub struct Area {

View file

@ -130,7 +130,7 @@ pub(crate) fn paint_icon(ui: &mut Ui, openness: f32, response: &Response) {
///
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// egui::CollapsingHeader::new("Heading")
/// .show(ui, |ui| {
/// ui.label("Contents");
@ -138,6 +138,7 @@ pub(crate) fn paint_icon(ui: &mut Ui, openness: f32, response: &Response) {
///
/// // Short version:
/// ui.collapsing("Heading", |ui| { ui.label("Contents"); });
/// # });
/// ```
#[must_use = "You should call .show()"]
pub struct CollapsingHeader {
@ -210,7 +211,7 @@ impl CollapsingHeader {
///
/// Example:
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// let mut selected = false;
/// let response = egui::CollapsingHeader::new("Select and open me")
/// .selectable(true)
@ -219,6 +220,7 @@ impl CollapsingHeader {
/// if response.header_response.clicked() {
/// selected = true;
/// }
/// # });
/// ```
pub fn selected(mut self, selected: bool) -> Self {
self.selected = selected;
@ -229,8 +231,9 @@ impl CollapsingHeader {
///
/// To show it behind all `CollapsingHeader` you can just use:
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.visuals_mut().collapsing_header_frame = true;
/// # });
/// ```
pub fn show_background(mut self, show_background: bool) -> Self {
self.show_background = show_background;

View file

@ -7,7 +7,7 @@ use epaint::Shape;
/// # #[derive(Debug, PartialEq)]
/// # enum Enum { First, Second, Third }
/// # let mut selected = Enum::First;
/// # let mut ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// egui::ComboBox::from_label("Select one!")
/// .selected_text(format!("{:?}", selected))
/// .show_ui(ui, |ui| {
@ -16,6 +16,7 @@ use epaint::Shape;
/// ui.selectable_value(&mut selected, Enum::Third, "Third");
/// }
/// );
/// # });
/// ```
#[must_use = "You should call .show*"]
pub struct ComboBox {
@ -109,7 +110,7 @@ impl ComboBox {
/// # #[derive(Debug, PartialEq)]
/// # enum Enum { First, Second, Third }
/// # let mut selected = Enum::First;
/// # let mut ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// let alternatives = ["a", "b", "c", "d"];
/// let mut selected = 2;
/// egui::ComboBox::from_label("Select one!").show_index(
@ -118,6 +119,7 @@ impl ComboBox {
/// alternatives.len(),
/// |i| alternatives[i].to_owned()
/// );
/// # });
/// ```
pub fn show_index(
self,

View file

@ -73,12 +73,11 @@ impl Side {
/// See the [module level docs](crate::containers::panel) for more details.
///
/// ```
/// # let mut ctx = egui::CtxRef::default();
/// # ctx.begin_frame(Default::default());
/// # let ctx = &ctx;
/// # egui::__run_test_ctx(|ctx| {
/// egui::SidePanel::left("my_left_panel").show(ctx, |ui| {
/// ui.label("Hello World!");
/// });
/// # });
/// ```
///
/// See also [`TopBottomPanel`].
@ -350,12 +349,11 @@ impl TopBottomSide {
/// See the [module level docs](crate::containers::panel) for more details.
///
/// ```
/// # let mut ctx = egui::CtxRef::default();
/// # ctx.begin_frame(Default::default());
/// # let ctx = &ctx;
/// # egui::__run_test_ctx(|ctx| {
/// egui::TopBottomPanel::top("my_panel").show(ctx, |ui| {
/// ui.label("Hello World!");
/// });
/// # });
/// ```
///
/// See also [`SidePanel`].
@ -605,12 +603,11 @@ impl TopBottomPanel {
/// See the [module level docs](crate::containers::panel) for more details.
///
/// ```
/// # let mut ctx = egui::CtxRef::default();
/// # ctx.begin_frame(Default::default());
/// # let ctx = &ctx;
/// # egui::__run_test_ctx(|ctx| {
/// egui::CentralPanel::default().show(ctx, |ui| {
/// ui.label("Hello World!");
/// });
/// # });
/// ```
#[must_use = "You should call .show()"]
#[derive(Default)]

View file

@ -58,12 +58,13 @@ impl MonoState {
/// Returns `None` if the tooltip could not be placed.
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// if ui.ui_contains_pointer() {
/// egui::show_tooltip(ui.ctx(), egui::Id::new("my_tooltip"), |ui| {
/// ui.label("Helpful text");
/// });
/// }
/// # });
/// ```
pub fn show_tooltip<R>(ctx: &CtxRef, id: Id, add_contents: impl FnOnce(&mut Ui) -> R) -> Option<R> {
show_tooltip_at_pointer(ctx, id, add_contents)
@ -78,12 +79,13 @@ pub fn show_tooltip<R>(ctx: &CtxRef, id: Id, add_contents: impl FnOnce(&mut Ui)
/// Returns `None` if the tooltip could not be placed.
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// if ui.ui_contains_pointer() {
/// egui::show_tooltip_at_pointer(ui.ctx(), egui::Id::new("my_tooltip"), |ui| {
/// ui.label("Helpful text");
/// });
/// }
/// # });
/// ```
pub fn show_tooltip_at_pointer<R>(
ctx: &CtxRef,
@ -221,10 +223,11 @@ fn show_tooltip_at_avoid_dyn<'c, R>(
/// Returns `None` if the tooltip could not be placed.
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// if ui.ui_contains_pointer() {
/// egui::show_tooltip_text(ui.ctx(), egui::Id::new("my_tooltip"), "Helpful text");
/// }
/// # });
/// ```
pub fn show_tooltip_text(ctx: &CtxRef, id: Id, text: impl Into<WidgetText>) -> Option<()> {
show_tooltip(ctx, id, |ui| {
@ -264,7 +267,7 @@ fn show_tooltip_area_dyn<'c, R>(
/// Returns `None` if the popup is not open.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// let response = ui.button("Open popup");
/// let popup_id = ui.make_persistent_id("my_unique_id");
/// if response.clicked() {
@ -275,6 +278,7 @@ fn show_tooltip_area_dyn<'c, R>(
/// ui.label("Some more info, or things you can select:");
/// ui.label("…");
/// });
/// # });
/// ```
pub fn popup_below_widget<R>(
ui: &Ui,

View file

@ -54,10 +54,12 @@ impl State {
/// Add vertical and/or horizontal scrolling to a contained [`Ui`].
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// egui::ScrollArea::vertical().show(ui, |ui| {
/// // Add a lot of widgets here.
/// });
/// # });
/// ```
#[derive(Clone, Debug)]
#[must_use = "You should call .show()"]
pub struct ScrollArea {
@ -370,7 +372,7 @@ impl ScrollArea {
/// Efficiently show only the visible part of a large number of rows.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// let text_style = egui::TextStyle::Body;
/// let row_height = ui.fonts()[text_style].row_height();
/// // let row_height = ui.spacing().interact_size.y; // if you are adding buttons instead of labels.
@ -381,6 +383,8 @@ impl ScrollArea {
/// ui.label(text);
/// }
/// });
/// # });
/// ```
pub fn show_rows<R>(
self,
ui: &mut Ui,

View file

@ -15,12 +15,11 @@ use super::*;
/// * if there should be a close button (none by default)
///
/// ```
/// # let mut ctx = egui::CtxRef::default();
/// # ctx.begin_frame(Default::default());
/// # let ctx = &ctx;
/// # egui::__run_test_ctx(|ctx| {
/// egui::Window::new("My Window").show(ctx, |ui| {
/// ui.label("Hello World!");
/// });
/// # });
#[must_use = "You should call .show()"]
pub struct Window<'open> {
title: WidgetText,

View file

@ -36,16 +36,14 @@ use epaint::{stats::*, text::Fonts, *};
/// // Game loop:
/// loop {
/// let raw_input = egui::RawInput::default();
/// ctx.begin_frame(raw_input);
///
/// let (output, shapes) = ctx.run(raw_input, |ctx| {
/// egui::CentralPanel::default().show(&ctx, |ui| {
/// ui.label("Hello world!");
/// if ui.button("Click me").clicked() {
/// /* take some action here */
/// // take some action here
/// }
/// });
///
/// let (output, shapes) = ctx.end_frame();
/// });
/// let clipped_meshes = ctx.tessellate(shapes); // create triangles to paint
/// handle_output(output);
/// paint(clipped_meshes);
@ -93,13 +91,34 @@ impl Default for CtxRef {
}
impl CtxRef {
/// Run the ui code for one frame.
///
/// Put your widgets into a [`SidePanel`], [`TopBottomPanel`], [`CentralPanel`], [`Window`] or [`Area`].
///
/// This will modify the internal reference to point to a new generation of [`Context`].
/// Any old clones of this [`CtxRef`] will refer to the old [`Context`], which will not get new input.
///
/// This is a convenience for calling [`Self::begin_frame`] and [`Context::end_frame`]
#[must_use]
pub fn run(
&mut self,
new_input: RawInput,
run_ui: impl FnOnce(&CtxRef),
) -> (Output, Vec<ClippedShape>) {
self.begin_frame(new_input);
run_ui(self);
self.end_frame()
}
/// Alternative to [`Self::run`].
///
/// Call at the start of every frame. Match with a call to [`Context::end_frame`].
///
/// This will modify the internal reference to point to a new generation of [`Context`].
/// Any old clones of this [`CtxRef`] will refer to the old [`Context`], which will not get new input.
///
/// Put your widgets into a [`SidePanel`], [`TopBottomPanel`], [`CentralPanel`], [`Window`] or [`Area`].
pub fn begin_frame(&mut self, new_input: RawInput) {
fn begin_frame(&mut self, new_input: RawInput) {
let mut self_: Context = (*self.0).clone();
self_.begin_frame_mut(new_input);
*self = Self(Arc::new(self_));
@ -577,10 +596,23 @@ impl Context {
self.input = input.begin_frame(new_raw_input);
self.frame_state.lock().begin_frame(&self.input);
{
// Load new fonts if required:
self.update_fonts(self.input.pixels_per_point());
// Ensure we register the background area so panels and background ui can catch clicks:
let screen_rect = self.input.screen_rect();
self.memory().areas.set_state(
LayerId::background(),
containers::area::State {
pos: screen_rect.min,
size: screen_rect.size(),
interactable: true,
},
);
}
/// Load fonts unless already loaded.
fn update_fonts(&mut self, pixels_per_point: f32) {
let new_font_definitions = self.memory().new_font_definitions.take();
let pixels_per_point = self.input.pixels_per_point();
let pixels_per_point_changed = match &self.fonts {
None => true,
@ -602,23 +634,11 @@ impl Context {
}
}
// Ensure we register the background area so panels and background ui can catch clicks:
let screen_rect = self.input.screen_rect();
self.memory().areas.set_state(
LayerId::background(),
containers::area::State {
pos: screen_rect.min,
size: screen_rect.size(),
interactable: true,
},
);
}
/// Call at the end of each frame.
/// Returns what has happened this frame [`crate::Output`] as well as what you need to paint.
/// You can transform the returned shapes into triangles with a call to [`Context::tessellate`].
#[must_use]
pub fn end_frame(&self) -> (Output, Vec<ClippedShape>) {
fn end_frame(&self) -> (Output, Vec<ClippedShape>) {
if self.input.wants_repaint() {
self.request_repaint();
}

View file

@ -238,7 +238,7 @@ impl GridLayout {
/// [`Ui::horizontal`], [`Ui::vertical`] etc.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// egui::Grid::new("some_unique_id").show(ui, |ui| {
/// ui.label("First row, first column");
/// ui.label("First row, second column");
@ -253,6 +253,7 @@ impl GridLayout {
/// ui.label("Third row, second column");
/// ui.end_row();
/// });
/// # });
/// ```
#[must_use = "You should call .show()"]
pub struct Grid {

View file

@ -237,7 +237,7 @@ impl InputState {
///
/// ```
/// # use egui::emath::Rot2;
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// let mut zoom = 1.0; // no zoom
/// let mut rotation = 0.0; // no rotation
/// if let Some(multi_touch) = ui.input().multi_touch() {
@ -245,6 +245,7 @@ impl InputState {
/// rotation += multi_touch.rotation_delta;
/// }
/// let transform = zoom * Rot2::from_angle(rotation);
/// # });
/// ```
///
/// By far not all touch devices are supported, and the details depend on the `egui`

View file

@ -108,11 +108,12 @@ impl Direction {
/// The layout of a [`Ui`][`crate::Ui`], e.g. "vertical & centered".
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.with_layout(egui::Layout::right_to_left(), |ui| {
/// ui.label("world!");
/// ui.label("Hello");
/// });
/// # });
/// ```
#[derive(Clone, Copy, Debug, PartialEq)]
// #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]

View file

@ -45,21 +45,21 @@
//! get access to an [`Ui`] where you can put widgets. For example:
//!
//! ```
//! # let mut ctx = egui::CtxRef::default();
//! # ctx.begin_frame(Default::default());
//! # 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 */
//! // take some action here
//! }
//! });
//! # });
//! ```
//!
//! ### Quick start
//!
//! ``` rust
//! # let ui = &mut egui::Ui::__test();
//! # egui::__run_test_ui(|ui| {
//! # let mut my_string = String::new();
//! # let mut my_boolean = true;
//! # let mut my_f32 = 42.0;
@ -89,6 +89,7 @@
//! ui.collapsing("Click to see what is hidden!", |ui| {
//! ui.label("Not much, as it turns out");
//! });
//! # });
//! ```
//!
//! ## Conventions
@ -117,16 +118,16 @@
//! // Game loop:
//! loop {
//! let raw_input: egui::RawInput = gather_input();
//! ctx.begin_frame(raw_input);
//!
//! let (output, shapes) = ctx.run(raw_input, |ctx| {
//! egui::CentralPanel::default().show(&ctx, |ui| {
//! ui.label("Hello world!");
//! if ui.button("Click me").clicked() {
//! /* take some action here */
//! // take some action here
//! }
//! });
//! });
//!
//! let (output, shapes) = ctx.end_frame();
//! let clipped_meshes = ctx.tessellate(shapes); // create triangles to paint
//! handle_output(output);
//! paint(clipped_meshes);
@ -141,10 +142,11 @@
//! Here is an example to illustrate it:
//!
//! ```
//! # let ui = &mut egui::Ui::__test();
//! # egui::__run_test_ui(|ui| {
//! if ui.button("click me").clicked() {
//! take_action()
//! }
//! # });
//! # fn take_action() {}
//! ```
//!
@ -166,17 +168,19 @@
//! ## How widgets works
//!
//! ```
//! # let ui = &mut egui::Ui::__test();
//! # egui::__run_test_ui(|ui| {
//! if ui.button("click me").clicked() { take_action() }
//! # });
//! # fn take_action() {}
//! ```
//!
//! is short for
//!
//! ```
//! # let ui = &mut egui::Ui::__test();
//! # egui::__run_test_ui(|ui| {
//! let button = egui::Button::new("click me");
//! if ui.add(button).clicked() { take_action() }
//! # });
//! # fn take_action() {}
//! ```
//!
@ -184,10 +188,11 @@
//!
//! ```
//! # use egui::Widget;
//! # let ui = &mut egui::Ui::__test();
//! # egui::__run_test_ui(|ui| {
//! let button = egui::Button::new("click me");
//! let response = button.ui(ui);
//! if response.clicked() { take_action() }
//! # });
//! # fn take_action() {}
//! ```
//!
@ -214,32 +219,35 @@
//! 3. Use a justified layout:
//!
//! ``` rust
//! # let ui = &mut egui::Ui::__test();
//! # egui::__run_test_ui(|ui| {
//! ui.with_layout(egui::Layout::top_down_justified(egui::Align::Center), |ui| {
//! ui.button("I am becoming wider as needed");
//! });
//! # });
//! ```
//!
//! 4. Fill in extra space with emptiness:
//!
//! ``` rust
//! # let ui = &mut egui::Ui::__test();
//! # egui::__run_test_ui(|ui| {
//! ui.allocate_space(ui.available_size()); // put this LAST in your panel/window code
//! # });
//! ```
//!
//! ## Sizes
//! You can control the size of widgets using [`Ui::add_sized`].
//!
//! ```
//! # let ui = &mut egui::Ui::__test();
//! # egui::__run_test_ui(|ui| {
//! # let mut my_value = 0.0_f32;
//! ui.add_sized([40.0, 20.0], egui::DragValue::new(&mut my_value));
//! # });
//! ```
//!
//! ## Code snippets
//!
//! ```
//! # let ui = &mut egui::Ui::__test();
//! # egui::__run_test_ui(|ui| {
//! # let mut some_bool = true;
//! // Miscellaneous tips and tricks
//!
@ -263,6 +271,7 @@
//!
//! ui.label("This text will be red, monospace, and won't wrap to a new line");
//! }); // the temporary settings are reverted here
//! # });
//! ```
// Forbid warnings in release builds:
@ -432,8 +441,9 @@ pub fn warn_if_debug_build(ui: &mut crate::Ui) {
/// Create a [`Hyperlink`](crate::Hyperlink) to the current [`file!()`] (and line) on Github
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.add(egui::github_link_file_line!("https://github.com/YOUR/PROJECT/blob/master/", "(source code)"));
/// # });
/// ```
#[macro_export]
macro_rules! github_link_file_line {
@ -446,8 +456,9 @@ macro_rules! github_link_file_line {
/// Create a [`Hyperlink`](crate::Hyperlink) to the current [`file!()`] on github.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.add(egui::github_link_file!("https://github.com/YOUR/PROJECT/blob/master/", "(source code)"));
/// # });
/// ```
#[macro_export]
macro_rules! github_link_file {
@ -462,7 +473,7 @@ macro_rules! github_link_file {
/// Show debug info on hover when [`Context::set_debug_on_hover`] has been turned on.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// // Turn on tracing of widgets
/// ui.ctx().set_debug_on_hover(true);
///
@ -471,6 +482,7 @@ macro_rules! github_link_file {
///
/// /// Show [`std::file`] and [`std::line`] on hover
/// egui::trace!(ui);
/// # });
/// ```
#[macro_export]
macro_rules! trace {
@ -561,3 +573,23 @@ pub enum WidgetType {
/// If this is something you think should be added, file an issue.
Other,
}
// ----------------------------------------------------------------------------
/// For use in tests; especially doctests.
pub fn __run_test_ctx(mut run_ui: impl FnMut(&CtxRef)) {
let mut ctx = CtxRef::default();
let _ = ctx.run(Default::default(), |ctx| {
run_ui(ctx);
});
}
/// For use in tests; especially doctests.
pub fn __run_test_ui(mut add_contents: impl FnMut(&mut Ui)) {
let mut ctx = CtxRef::default();
let _ = ctx.run(Default::default(), |ctx| {
crate::CentralPanel::default().show(ctx, |ui| {
add_contents(ui);
});
});
}

View file

@ -200,13 +200,14 @@ impl Response {
/// or (in case of a [`crate::TextEdit`]) because the user pressed enter.
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// # let mut my_text = String::new();
/// # fn do_request(_: &str) {}
/// let response = ui.text_edit_singleline(&mut my_text);
/// if response.lost_focus() && ui.input().key_pressed(egui::Key::Enter) {
/// do_request(&my_text);
/// }
/// # });
/// ```
pub fn lost_focus(&self) -> bool {
self.ctx.memory().lost_focus(self.id)
@ -415,11 +416,12 @@ impl Response {
/// it is better to give the widget a `Sense` instead, e.g. using [`crate::Label::sense`].
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// let response = ui.label("hello");
/// assert!(!response.clicked()); // labels don't sense clicks by default
/// let response = response.interact(egui::Sense::click());
/// if response.clicked() { /* … */ }
/// # });
/// ```
pub fn interact(&self, sense: Sense) -> Self {
self.ctx.interact_with_hovered(
@ -436,7 +438,7 @@ impl Response {
///
/// ```
/// # use egui::Align;
/// # let mut ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// egui::ScrollArea::vertical().show(ui, |ui| {
/// for i in 0..1000 {
/// let response = ui.button(format!("Button {}", i));
@ -445,6 +447,7 @@ impl Response {
/// }
/// }
/// });
/// # });
/// ```
pub fn scroll_to_me(&self, align: Align) {
let scroll_target = lerp(self.rect.x_range(), align.to_factor());
@ -478,13 +481,14 @@ impl Response {
/// Response to secondary clicks (right-clicks) by showing the given menu.
///
/// ``` rust
/// # let mut ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// let response = ui.label("Right-click me!");
/// response.context_menu(|ui|{
/// response.context_menu(|ui| {
/// if ui.button("Close the menu").clicked() {
/// ui.close_menu();
/// }
/// });
/// # });
/// ```
pub fn context_menu(self, add_contents: impl FnOnce(&mut Ui)) -> Self {
self.ctx.show_context_menu(&self, add_contents);
@ -549,12 +553,13 @@ impl std::ops::BitOr for Response {
/// To summarize the response from many widgets you can use this pattern:
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// # let (widget_a, widget_b, widget_c) = (egui::Label::new("a"), egui::Label::new("b"), egui::Label::new("c"));
/// let mut response = ui.add(widget_a);
/// response |= ui.add(widget_b);
/// response |= ui.add(widget_c);
/// if response.hovered() { ui.label("You hovered at least one of the widgets"); }
/// # });
/// ```
impl std::ops::BitOrAssign for Response {
fn bitor_assign(&mut self, rhs: Self) {
@ -568,13 +573,14 @@ impl std::ops::BitOrAssign for Response {
/// the results of the inner function and the ui as a whole, e.g.:
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// let inner_resp = ui.horizontal(|ui| {
/// ui.label("Blah blah");
/// 42
/// });
/// inner_resp.response.on_hover_text("You hovered the horizontal layout");
/// assert_eq!(inner_resp.inner, 42);
/// # });
/// ```
#[derive(Debug)]
pub struct InnerResponse<R> {

View file

@ -16,7 +16,7 @@ use crate::{
/// Represents a region of the screen with a type of layout (horizontal or vertical).
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.add(egui::Label::new("Hello World!"));
/// ui.label("A shorter and more convenient way to add a label.");
/// ui.horizontal(|ui| {
@ -25,6 +25,7 @@ use crate::{
/// /* … */
/// }
/// });
/// # });
/// ```
pub struct Ui {
/// ID of this ui.
@ -109,16 +110,6 @@ impl Ui {
}
}
/// Empty `Ui` for use in tests.
pub fn __test() -> Self {
let mut ctx = CtxRef::default();
ctx.begin_frame(Default::default());
let id = Id::new("__test");
let layer_id = LayerId::new(Order::Middle, id);
let rect = Rect::from_min_size(Pos2::new(0.0, 0.0), vec2(1000.0, 1000.0));
Self::new(ctx, layer_id, id, rect, rect)
}
// -------------------------------------------------
/// A unique identity of this `Ui`.
@ -140,8 +131,9 @@ impl Ui {
///
/// Example:
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.style_mut().body_text_style = egui::TextStyle::Heading;
/// # });
/// ```
pub fn style_mut(&mut self) -> &mut Style {
std::sync::Arc::make_mut(&mut self.style) // clone-on-write
@ -171,8 +163,9 @@ impl Ui {
///
/// Example:
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.spacing_mut().item_spacing = egui::vec2(10.0, 2.0);
/// # });
/// ```
pub fn spacing_mut(&mut self) -> &mut crate::style::Spacing {
&mut self.style_mut().spacing
@ -192,8 +185,9 @@ impl Ui {
///
/// Example:
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.visuals_mut().override_text_color = Some(egui::Color32::RED);
/// # });
/// ```
pub fn visuals_mut(&mut self) -> &mut crate::Visuals {
&mut self.style_mut().visuals
@ -227,7 +221,7 @@ impl Ui {
///
/// ### Example
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// # let mut enabled = true;
/// ui.group(|ui| {
/// ui.checkbox(&mut enabled, "Enable subsection");
@ -236,6 +230,7 @@ impl Ui {
/// /* … */
/// }
/// });
/// # });
/// ```
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled &= enabled;
@ -260,7 +255,7 @@ impl Ui {
///
/// ### Example
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// # let mut visible = true;
/// ui.group(|ui| {
/// ui.checkbox(&mut visible, "Show subsection");
@ -269,6 +264,7 @@ impl Ui {
/// /* … */
/// }
/// });
/// # });
/// ```
pub fn set_visible(&mut self, visible: bool) {
self.set_enabled(visible);
@ -593,10 +589,11 @@ impl Ui {
/// You will never get a rectangle that is smaller than the amount of space you asked for.
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// let response = ui.allocate_response(egui::vec2(100.0, 200.0), egui::Sense::click());
/// if response.clicked() { /* … */ }
/// ui.painter().rect_stroke(response.rect, 0.0, (1.0, egui::Color32::WHITE));
/// # });
/// ```
pub fn allocate_response(&mut self, desired_size: Vec2, sense: Sense) -> Response {
let (id, rect) = self.allocate_space(desired_size);
@ -640,9 +637,10 @@ impl Ui {
/// Returns an automatic `Id` (which you can use for interaction) and the `Rect` of where to put your widget.
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// let (id, rect) = ui.allocate_space(egui::vec2(100.0, 200.0));
/// let response = ui.interact(rect, id, egui::Sense::click());
/// # });
/// ```
pub fn allocate_space(&mut self, desired_size: Vec2) -> (Id, Rect) {
// For debug rendering
@ -833,8 +831,8 @@ impl Ui {
///
/// ```
/// # use egui::*;
/// # let mut ui = &mut egui::Ui::__test();
/// # use std::f32::consts::TAU;
/// # egui::__run_test_ui(|ui| {
/// let size = Vec2::splat(16.0);
/// let (response, painter) = ui.allocate_painter(size, Sense::hover());
/// let rect = response.rect;
@ -846,6 +844,7 @@ impl Ui {
/// painter.line_segment([c - vec2(0.0, r), c + vec2(0.0, r)], stroke);
/// painter.line_segment([c, c + r * Vec2::angled(TAU * 1.0 / 8.0)], stroke);
/// painter.line_segment([c, c + r * Vec2::angled(TAU * 3.0 / 8.0)], stroke);
/// # });
/// ```
pub fn allocate_painter(&mut self, desired_size: Vec2, sense: Sense) -> (Response, Painter) {
let response = self.allocate_response(desired_size, sense);
@ -858,7 +857,7 @@ impl Ui {
///
/// ```
/// # use egui::Align;
/// # let mut ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// egui::ScrollArea::vertical().show(ui, |ui| {
/// let scroll_bottom = ui.button("Scroll to bottom.").clicked();
/// for i in 0..1000 {
@ -869,6 +868,7 @@ impl Ui {
/// ui.scroll_to_cursor(Align::BOTTOM);
/// }
/// });
/// # });
/// ```
pub fn scroll_to_cursor(&mut self, align: Align) {
let target = self.next_widget_position();
@ -888,10 +888,11 @@ impl Ui {
/// See also [`Self::add_sized`] and [`Self::put`].
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// # let mut my_value = 42;
/// let response = ui.add(egui::Slider::new(&mut my_value, 0..=100));
/// response.on_hover_text("Drag me!");
/// # });
/// ```
#[inline]
pub fn add(&mut self, widget: impl Widget) -> Response {
@ -906,9 +907,10 @@ impl Ui {
/// See also [`Self::add`] and [`Self::put`].
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # let mut my_value = 42;
/// # egui::__run_test_ui(|ui| {
/// ui.add_sized([40.0, 20.0], egui::DragValue::new(&mut my_value));
/// # });
/// ```
pub fn add_sized(&mut self, max_size: impl Into<Vec2>, widget: impl Widget) -> Response {
// TODO: configure to overflow to main_dir instead of centered overflow
@ -939,8 +941,9 @@ impl Ui {
/// See also [`Self::add_enabled_ui`] and [`Self::is_enabled`].
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.add_enabled(false, egui::Button::new("Can't click this"));
/// # });
/// ```
pub fn add_enabled(&mut self, enabled: bool, widget: impl Widget) -> Response {
if enabled || !self.is_enabled() {
@ -964,7 +967,7 @@ impl Ui {
///
/// ### Example
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// # let mut enabled = true;
/// ui.checkbox(&mut enabled, "Enable subsection");
/// ui.add_enabled_ui(enabled, |ui| {
@ -972,6 +975,7 @@ impl Ui {
/// /* … */
/// }
/// });
/// # });
/// ```
pub fn add_enabled_ui<R>(
&mut self,
@ -1061,8 +1065,9 @@ impl Ui {
/// Shortcut for `add(Hyperlink::new(url).text(label))`
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.hyperlink_to("egui on GitHub", "https://www.github.com/emilk/egui/");
/// # });
/// ```
///
/// See also [`Hyperlink`].
@ -1106,7 +1111,7 @@ impl Ui {
/// See also [`Button`].
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// if ui.button("Click me!").clicked() {
/// // …
/// }
@ -1115,6 +1120,7 @@ impl Ui {
/// if ui.button(RichText::new("delete").color(Color32::RED)).clicked() {
/// // …
/// }
/// # });
/// ```
#[must_use = "You should check if the user clicked this with `if ui.button(…).clicked() { … } "]
#[inline]
@ -1150,7 +1156,7 @@ impl Ui {
/// If clicked, `selected_value` is assigned to `*current_value`.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
///
/// #[derive(PartialEq)]
/// enum Enum { First, Second, Third }
@ -1163,6 +1169,7 @@ impl Ui {
/// if ui.add(egui::RadioButton::new(my_enum == Enum::First, "First")).clicked() {
/// my_enum = Enum::First
/// }
/// # });
/// ```
pub fn radio_value<Value: PartialEq>(
&mut self,
@ -1349,10 +1356,11 @@ impl Ui {
/// Put into a [`Frame::group`], visually grouping the contents together
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.group(|ui| {
/// ui.label("Within a frame");
/// });
/// # });
/// ```
///
/// Se also [`Self::scope`].
@ -1365,11 +1373,12 @@ impl Ui {
/// You can use this to temporarily change the [`Style`] of a sub-region, for instance:
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.scope(|ui| {
/// ui.spacing_mut().slider_width = 200.0; // Temporary change
/// // …
/// });
/// # });
/// ```
pub fn scope<R>(&mut self, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {
self.scope_dyn(Box::new(add_contents))
@ -1483,11 +1492,12 @@ impl Ui {
/// It also contains the `Rect` used by the horizontal layout.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.horizontal(|ui| {
/// ui.label("Same");
/// ui.label("row");
/// });
/// # });
/// ```
///
/// See also [`Self::with_layout`] for more options.
@ -1557,11 +1567,12 @@ impl Ui {
/// Widgets will be left-justified.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.vertical(|ui| {
/// ui.label("over");
/// ui.label("under");
/// });
/// # });
/// ```
///
/// See also [`Self::with_layout`] for more options.
@ -1574,11 +1585,12 @@ impl Ui {
/// Widgets will be horizontally centered.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.vertical_centered(|ui| {
/// ui.label("over");
/// ui.label("under");
/// });
/// # });
/// ```
#[inline]
pub fn vertical_centered<R>(
@ -1592,11 +1604,12 @@ impl Ui {
/// Widgets will be horizontally centered and justified (fill full width).
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.vertical_centered_justified(|ui| {
/// ui.label("over");
/// ui.label("under");
/// });
/// # });
/// ```
pub fn vertical_centered_justified<R>(
&mut self,
@ -1611,11 +1624,12 @@ impl Ui {
/// The new layout will take up all available space.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.with_layout(egui::Layout::right_to_left(), |ui| {
/// ui.label("world!");
/// ui.label("Hello");
/// });
/// # });
/// ```
///
/// See also [`Self::allocate_ui_with_layout`],
@ -1687,11 +1701,12 @@ impl Ui {
/// Temporarily split split an Ui into several columns.
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.columns(2, |columns| {
/// columns[0].label("First column");
/// columns[1].label("Second column");
/// });
/// # });
/// ```
#[inline]
pub fn columns<R>(
@ -1764,7 +1779,7 @@ impl Ui {
/// Create a menu button. Creates a button for a sub-menu when the `Ui` is inside a menu.
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.menu_button("My menu", |ui| {
/// ui.menu_button("My sub-menu", |ui| {
/// if ui.button("Close the menu").clicked() {
@ -1772,6 +1787,7 @@ impl Ui {
/// }
/// });
/// });
/// # });
/// ```
pub fn menu_button<R>(
&mut self,

View file

@ -5,7 +5,7 @@ use crate::*;
/// See also [`Ui::button`].
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// # fn do_stuff() {}
///
/// if ui.add(egui::Button::new("Click me")).clicked() {
@ -16,6 +16,7 @@ use crate::*;
/// if ui.add_enabled(false, egui::Button::new("Can't click this")).clicked() {
/// unreachable!();
/// }
/// # });
/// ```
#[must_use = "You should put this widget in an ui with `ui.add(widget);`"]
pub struct Button {
@ -177,11 +178,12 @@ impl Widget for Button {
/// Usually you'd use [`Ui::checkbox`] instead.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// # let mut my_bool = true;
/// // These are equivalent:
/// ui.checkbox(&mut my_bool, "Checked");
/// ui.add(egui::Checkbox::new(&mut my_bool, "Checked"));
/// # });
/// ```
#[must_use = "You should put this widget in an ui with `ui.add(widget);`"]
pub struct Checkbox<'a> {
@ -275,7 +277,7 @@ impl<'a> Widget for Checkbox<'a> {
/// Usually you'd use [`Ui::radio_value`] or [`Ui::radio`] instead.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// #[derive(PartialEq)]
/// enum Enum { First, Second, Third }
/// let mut my_enum = Enum::First;
@ -287,6 +289,7 @@ impl<'a> Widget for Checkbox<'a> {
/// if ui.add(egui::RadioButton::new(my_enum == Enum::First, "First")).clicked() {
/// my_enum = Enum::First
/// }
/// # });
/// ```
#[must_use = "You should put this widget in an ui with `ui.add(widget);`"]
pub struct RadioButton {

View file

@ -42,9 +42,10 @@ fn set(get_set_value: &mut GetSetValue<'_>, value: f64) {
/// A numeric value that you can change by dragging the number. More compact than a [`Slider`].
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// # let mut my_f32: f32 = 0.0;
/// ui.add(egui::DragValue::new(&mut my_f32).speed(0.1));
/// # });
/// ```
#[must_use = "You should put this widget in an ui with `ui.add(widget);`"]
pub struct DragValue<'a> {

View file

@ -5,9 +5,10 @@ use crate::*;
/// See also [`Ui::hyperlink`] and [`Ui::hyperlink_to`].
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.hyperlink("https://github.com/emilk/egui");
/// ui.add(egui::Hyperlink::new("https://github.com/emilk/egui").text("My favorite repo").small());
/// # });
/// ```
#[must_use = "You should put this widget in an ui with `ui.add(widget);`"]
pub struct Hyperlink {

View file

@ -3,12 +3,13 @@ use crate::*;
/// An widget to show an image of a given size.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// # let my_texture_id = egui::TextureId::User(0);
/// ui.add(egui::Image::new(my_texture_id, [640.0, 480.0]));
///
/// // Shorter version:
/// ui.image(my_texture_id, [640.0, 480.0]);
/// # });
/// ```
///
/// Se also [`crate::ImageButton`].

View file

@ -3,11 +3,12 @@ use crate::{widget_text::WidgetTextGalley, *};
/// Static text.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// ui.label("Equivalent");
/// ui.add(egui::Label::new("Equivalent"));
/// ui.add(egui::Label::new("With Options").wrap(false));
/// ui.label(egui::RichText::new("With formatting").underline());
/// # });
/// ```
#[must_use = "You should put this widget in an ui with `ui.add(widget);`"]
pub struct Label {
@ -134,10 +135,11 @@ impl Label {
///
/// ``` rust
/// # use egui::{Label, Sense};
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// if ui.add(Label::new("click me").sense(Sense::click())).clicked() {
/// /* … */
/// }
/// # });
/// ```
pub fn sense(mut self, sense: Sense) -> Self {
self.sense = sense;

View file

@ -47,7 +47,7 @@ impl PlotMemory {
/// `Plot` supports multiple lines and points.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// use egui::plot::{Line, Plot, Value, Values};
/// let sin = (0..1000).map(|i| {
/// let x = i as f64 * 0.01;
@ -57,6 +57,7 @@ impl PlotMemory {
/// ui.add(
/// Plot::new("my_plot").line(line).view_aspect(2.0)
/// );
/// # });
/// ```
pub struct Plot {
id_source: Id,

View file

@ -7,7 +7,7 @@ use crate::*;
/// Usually you'd use [`Ui::selectable_value`] or [`Ui::selectable_label`] instead.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// #[derive(PartialEq)]
/// enum Enum { First, Second, Third }
/// let mut my_enum = Enum::First;
@ -19,6 +19,7 @@ use crate::*;
/// if ui.add(egui::SelectableLabel::new(my_enum == Enum::First, "First")).clicked() {
/// my_enum = Enum::First
/// }
/// # });
/// ```
#[must_use = "You should put this widget in an ui with `ui.add(widget);`"]
pub struct SelectableLabel {

View file

@ -5,10 +5,11 @@ use crate::*;
/// Usually you'd use the shorter version [`Ui::separator`].
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// // These are equivalent:
/// ui.separator();
/// ui.add(egui::Separator::default());
/// # });
/// ```
#[must_use = "You should put this widget in an ui with `ui.add(widget);`"]
pub struct Separator {

View file

@ -45,9 +45,10 @@ struct SliderSpec {
/// The user can click the value display to edit its value. It can be turned off with `.show_value(false)`.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// # let mut my_f32: f32 = 0.0;
/// ui.add(egui::Slider::new(&mut my_f32, 0.0..=100.0).text("My value"));
/// # });
/// ```
///
/// The default `Slider` size is set by [`crate::style::Spacing::slider_width`].

View file

@ -13,7 +13,7 @@ use super::{CCursorRange, CursorRange, TextEditOutput, TextEditState};
/// Example:
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// # let mut my_string = String::new();
/// let response = ui.add(egui::TextEdit::singleline(&mut my_string));
/// if response.changed() {
@ -22,14 +22,16 @@ use super::{CCursorRange, CursorRange, TextEditOutput, TextEditState};
/// if response.lost_focus() && ui.input().key_pressed(egui::Key::Enter) {
/// // …
/// }
/// # });
/// ```
///
/// To fill an [`Ui`] with a [`TextEdit`] use [`Ui::add_sized`]:
///
/// ```
/// # let mut ui = egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// # let mut my_string = String::new();
/// ui.add_sized(ui.available_size(), egui::TextEdit::multiline(&mut my_string));
/// # });
/// ```
///
///
@ -165,7 +167,7 @@ impl<'t> TextEdit<'t> {
/// the text and the wrap width.
///
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # egui::__run_test_ui(|ui| {
/// # let mut my_code = String::new();
/// # fn my_memoized_highlighter(s: &str) -> egui::text::LayoutJob { Default::default() }
/// let mut layouter = |ui: &egui::Ui, string: &str, wrap_width: f32| {
@ -174,6 +176,7 @@ impl<'t> TextEdit<'t> {
/// ui.fonts().layout_job(layout_job)
/// };
/// ui.add(egui::TextEdit::multiline(&mut my_code).layouter(&mut layouter));
/// # });
/// ```
pub fn layouter(mut self, layouter: &'t mut dyn FnMut(&Ui, &str, f32) -> Arc<Galley>) -> Self {
self.layouter = Some(layouter);

View file

@ -13,24 +13,24 @@ pub fn criterion_benchmark(c: &mut Criterion) {
// The most end-to-end benchmark.
c.bench_function("demo_with_tessellate__realistic", |b| {
b.iter(|| {
ctx.begin_frame(raw_input.clone());
demo_windows.ui(&ctx);
let (_, shapes) = ctx.end_frame();
let (_output, shapes) = ctx.run(raw_input.clone(), |ctx| {
demo_windows.ui(ctx);
});
ctx.tessellate(shapes)
})
});
c.bench_function("demo_no_tessellate", |b| {
b.iter(|| {
ctx.begin_frame(raw_input.clone());
demo_windows.ui(&ctx);
ctx.end_frame()
ctx.run(raw_input.clone(), |ctx| {
demo_windows.ui(ctx);
})
})
});
ctx.begin_frame(raw_input.clone());
demo_windows.ui(&ctx);
let (_, shapes) = ctx.end_frame();
let (_output, shapes) = ctx.run(raw_input.clone(), |ctx| {
demo_windows.ui(ctx);
});
c.bench_function("demo_only_tessellate", |b| {
b.iter(|| ctx.tessellate(shapes.clone()))
});
@ -42,17 +42,17 @@ pub fn criterion_benchmark(c: &mut Criterion) {
let mut demo_windows = egui_demo_lib::DemoWindows::default();
c.bench_function("demo_full_no_tessellate", |b| {
b.iter(|| {
ctx.begin_frame(raw_input.clone());
demo_windows.ui(&ctx);
ctx.end_frame()
ctx.run(raw_input.clone(), |ctx| {
demo_windows.ui(ctx);
})
})
});
}
{
let mut ctx = egui::CtxRef::default();
ctx.begin_frame(raw_input);
let mut ui = egui::Ui::__test();
let _ = ctx.run(raw_input, |ctx| {
egui::CentralPanel::default().show(ctx, |ui| {
c.bench_function("label &str", |b| {
b.iter(|| {
ui.label("the quick brown fox jumps over the lazy dog");
@ -63,6 +63,8 @@ pub fn criterion_benchmark(c: &mut Criterion) {
ui.label("the quick brown fox jumps over the lazy dog".to_owned());
})
});
});
});
}
{

View file

@ -145,9 +145,9 @@ fn test_egui_e2e() {
const NUM_FRAMES: usize = 5;
for _ in 0..NUM_FRAMES {
ctx.begin_frame(raw_input.clone());
demo_windows.ui(&ctx);
let (_output, shapes) = ctx.end_frame();
let (_output, shapes) = ctx.run(raw_input.clone(), |ctx| {
demo_windows.ui(ctx);
});
let clipped_meshes = ctx.tessellate(shapes);
assert!(!clipped_meshes.is_empty());
}
@ -164,9 +164,9 @@ fn test_egui_zero_window_size() {
const NUM_FRAMES: usize = 5;
for _ in 0..NUM_FRAMES {
ctx.begin_frame(raw_input.clone());
demo_windows.ui(&ctx);
let (_output, shapes) = ctx.end_frame();
let (_output, shapes) = ctx.run(raw_input.clone(), |ctx| {
demo_windows.ui(ctx);
});
let clipped_meshes = ctx.tessellate(shapes);
assert!(clipped_meshes.is_empty(), "There should be nothing to show");
}

View file

@ -101,7 +101,7 @@ use glium::glutin;
// ----------------------------------------------------------------------------
/// Use [`egui`] from a [`glium`] app.
/// Convenience wrapper for using [`egui`] from a [`glium`] app.
pub struct EguiGlium {
pub egui_ctx: egui::CtxRef,
pub egui_winit: egui_winit::State,
@ -136,16 +136,12 @@ impl EguiGlium {
pub fn run(
&mut self,
display: &glium::Display,
mut run_ui: impl FnMut(&egui::CtxRef),
run_ui: impl FnMut(&egui::CtxRef),
) -> (bool, Vec<egui::epaint::ClippedShape>) {
let raw_input = self
.egui_winit
.take_egui_input(display.gl_window().window());
self.egui_ctx.begin_frame(raw_input);
run_ui(&self.egui_ctx);
let (egui_output, shapes) = self.egui_ctx.end_frame();
let (egui_output, shapes) = self.egui_ctx.run(raw_input, run_ui);
let needs_repaint = egui_output.needs_repaint;
self.egui_winit
.handle_output(display.gl_window().window(), &self.egui_ctx, egui_output);

View file

@ -150,14 +150,10 @@ impl EguiGlow {
pub fn run(
&mut self,
window: &glutin::window::Window,
mut run_ui: impl FnMut(&egui::CtxRef),
run_ui: impl FnMut(&egui::CtxRef),
) -> (bool, Vec<egui::epaint::ClippedShape>) {
let raw_input = self.egui_winit.take_egui_input(window);
self.egui_ctx.begin_frame(raw_input);
run_ui(&self.egui_ctx);
let (egui_output, shapes) = self.egui_ctx.end_frame();
let (egui_output, shapes) = self.egui_ctx.run(raw_input, run_ui);
let needs_repaint = egui_output.needs_repaint;
self.egui_winit
.handle_output(window, &self.egui_ctx, egui_output);

View file

@ -189,8 +189,6 @@ impl AppRunner {
let canvas_size = canvas_size_in_points(self.canvas_id());
let raw_input = self.input.new_frame(canvas_size);
self.egui_ctx.begin_frame(raw_input);
let mut app_output = epi::backend::AppOutput::default();
let mut frame = epi::backend::FrameBuilder {
info: self.integration_info(),
@ -200,9 +198,10 @@ impl AppRunner {
}
.build();
self.app.update(&self.egui_ctx, &mut frame);
let (egui_output, shapes) = self.egui_ctx.end_frame();
let app = &mut self.app; // TODO: remove when we bump MSRV to 1.56
let (egui_output, shapes) = self.egui_ctx.run(raw_input, |egui_ctx| {
app.update(egui_ctx, &mut frame);
});
let clipped_meshes = self.egui_ctx.tessellate(shapes);
self.handle_egui_output(&egui_output);