mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 05:40:03 -04:00
Introduce egui::FullOutput, returned from Context::run (#1292)
* Introduce `egui::FullOutput`, returned from `Context::run` * Rename `Output` to `PlatformOutput`
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
// #![warn(missing_docs)]
|
||||
|
||||
use crate::{
|
||||
animation_manager::AnimationManager, data::output::Output, frame_state::FrameState,
|
||||
input_state::*, layers::GraphicLayers, memory::Options, TextureHandle, *,
|
||||
animation_manager::AnimationManager, data::output::PlatformOutput, frame_state::FrameState,
|
||||
input_state::*, layers::GraphicLayers, memory::Options, output::FullOutput, TextureHandle, *,
|
||||
};
|
||||
use epaint::{mutex::*, stats::*, text::Fonts, TessellationOptions, *};
|
||||
|
||||
@@ -42,7 +42,7 @@ struct ContextImpl {
|
||||
|
||||
// The output of a frame:
|
||||
graphics: GraphicLayers,
|
||||
output: Output,
|
||||
output: PlatformOutput,
|
||||
|
||||
paint_stats: PaintStats,
|
||||
|
||||
@@ -108,7 +108,7 @@ impl ContextImpl {
|
||||
/// Your handle to egui.
|
||||
///
|
||||
/// This is the first thing you need when working with egui.
|
||||
/// Contains the [`InputState`], [`Memory`], [`Output`], and more.
|
||||
/// Contains the [`InputState`], [`Memory`], [`PlatformOutput`], and more.
|
||||
///
|
||||
/// [`Context`] is cheap to clone, and any clones refers to the same mutable data
|
||||
/// ([`Context`] uses refcounting internally).
|
||||
@@ -121,14 +121,14 @@ impl ContextImpl {
|
||||
/// # Example:
|
||||
///
|
||||
/// ``` no_run
|
||||
/// # fn handle_output(_: egui::Output) {}
|
||||
/// # fn paint(_: Vec<egui::ClippedMesh>) {}
|
||||
/// # fn handle_platform_output(_: egui::PlatformOutput) {}
|
||||
/// # fn paint(textures_detla: egui::TexturesDelta, _: Vec<egui::ClippedMesh>) {}
|
||||
/// let mut ctx = egui::Context::default();
|
||||
///
|
||||
/// // Game loop:
|
||||
/// loop {
|
||||
/// let raw_input = egui::RawInput::default();
|
||||
/// let (output, shapes) = ctx.run(raw_input, |ctx| {
|
||||
/// let full_output = ctx.run(raw_input, |ctx| {
|
||||
/// egui::CentralPanel::default().show(&ctx, |ui| {
|
||||
/// ui.label("Hello world!");
|
||||
/// if ui.button("Click me").clicked() {
|
||||
@@ -136,9 +136,9 @@ impl ContextImpl {
|
||||
/// }
|
||||
/// });
|
||||
/// });
|
||||
/// let clipped_meshes = ctx.tessellate(shapes); // create triangles to paint
|
||||
/// handle_output(output);
|
||||
/// paint(clipped_meshes);
|
||||
/// handle_platform_output(full_output.platform_output);
|
||||
/// let clipped_meshes = ctx.tessellate(full_output.shapes); // create triangles to paint
|
||||
/// paint(full_output.textures_delta, clipped_meshes);
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
@@ -185,19 +185,15 @@ impl Context {
|
||||
///
|
||||
/// // Each frame:
|
||||
/// let input = egui::RawInput::default();
|
||||
/// let (output, shapes) = ctx.run(input, |ctx| {
|
||||
/// let full_output = ctx.run(input, |ctx| {
|
||||
/// egui::CentralPanel::default().show(&ctx, |ui| {
|
||||
/// ui.label("Hello egui!");
|
||||
/// });
|
||||
/// });
|
||||
/// // handle output, paint shapes
|
||||
/// // handle full_output
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn run(
|
||||
&self,
|
||||
new_input: RawInput,
|
||||
run_ui: impl FnOnce(&Context),
|
||||
) -> (Output, Vec<ClippedShape>) {
|
||||
pub fn run(&self, new_input: RawInput, run_ui: impl FnOnce(&Context)) -> FullOutput {
|
||||
self.begin_frame(new_input);
|
||||
run_ui(self);
|
||||
self.end_frame()
|
||||
@@ -217,8 +213,8 @@ impl Context {
|
||||
/// ui.label("Hello egui!");
|
||||
/// });
|
||||
///
|
||||
/// let (output, shapes) = ctx.end_frame();
|
||||
/// // handle output, paint shapes
|
||||
/// let full_output = ctx.end_frame();
|
||||
/// // handle full_output
|
||||
/// ```
|
||||
pub fn begin_frame(&self, new_input: RawInput) {
|
||||
self.write().begin_frame_mut(new_input);
|
||||
@@ -463,8 +459,13 @@ impl Context {
|
||||
}
|
||||
|
||||
/// What egui outputs each frame.
|
||||
///
|
||||
/// ```
|
||||
/// # let mut ctx = egui::Context::default();
|
||||
/// ctx.output().cursor_icon = egui::CursorIcon::Progress;
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn output(&self) -> RwLockWriteGuard<'_, Output> {
|
||||
pub fn output(&self) -> RwLockWriteGuard<'_, PlatformOutput> {
|
||||
RwLockWriteGuard::map(self.write(), |c| &mut c.output)
|
||||
}
|
||||
|
||||
@@ -719,14 +720,13 @@ impl Context {
|
||||
|
||||
impl Context {
|
||||
/// 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>) {
|
||||
pub fn end_frame(&self) -> FullOutput {
|
||||
if self.input().wants_repaint() {
|
||||
self.request_repaint();
|
||||
}
|
||||
|
||||
let textures_delta;
|
||||
{
|
||||
let ctx_impl = &mut *self.write();
|
||||
ctx_impl
|
||||
@@ -742,20 +742,26 @@ impl Context {
|
||||
.set(TextureId::default(), font_image_delta);
|
||||
}
|
||||
|
||||
ctx_impl
|
||||
.output
|
||||
.textures_delta
|
||||
.append(ctx_impl.tex_manager.0.write().take_delta());
|
||||
}
|
||||
textures_delta = ctx_impl.tex_manager.0.write().take_delta();
|
||||
};
|
||||
|
||||
let mut output: Output = std::mem::take(&mut self.output());
|
||||
if self.read().repaint_requests > 0 {
|
||||
let platform_output: PlatformOutput = std::mem::take(&mut self.output());
|
||||
|
||||
let needs_repaint = if self.read().repaint_requests > 0 {
|
||||
self.write().repaint_requests -= 1;
|
||||
output.needs_repaint = true;
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let shapes = self.drain_paint_lists();
|
||||
(output, shapes)
|
||||
|
||||
FullOutput {
|
||||
platform_output,
|
||||
needs_repaint,
|
||||
textures_delta,
|
||||
shapes,
|
||||
}
|
||||
}
|
||||
|
||||
fn drain_paint_lists(&self) -> Vec<ClippedShape> {
|
||||
|
||||
@@ -2,11 +2,56 @@
|
||||
|
||||
use crate::WidgetType;
|
||||
|
||||
/// What egui emits each frame.
|
||||
/// What egui emits each frame from [`crate::Context::run`].
|
||||
///
|
||||
/// The backend should use this.
|
||||
#[derive(Clone, Default, PartialEq)]
|
||||
pub struct FullOutput {
|
||||
/// Non-rendering related output.
|
||||
pub platform_output: PlatformOutput,
|
||||
|
||||
/// If `true`, egui is requesting immediate repaint (i.e. on the next frame).
|
||||
///
|
||||
/// This happens for instance when there is an animation, or if a user has called `Context::request_repaint()`.
|
||||
pub needs_repaint: bool,
|
||||
|
||||
/// Texture changes since last frame (including the font texture).
|
||||
///
|
||||
/// The backend needs to apply [`crate::TexturesDelta::set`] _before_ painting,
|
||||
/// and free any texture in [`crate::TexturesDelta::free`] _after_ painting.
|
||||
pub textures_delta: epaint::textures::TexturesDelta,
|
||||
|
||||
/// What to paint.
|
||||
///
|
||||
/// You can use [`crate::Context::tessellate`] to turn this into triangles.
|
||||
pub shapes: Vec<epaint::ClippedShape>,
|
||||
}
|
||||
|
||||
impl FullOutput {
|
||||
/// Add on new output.
|
||||
pub fn append(&mut self, newer: Self) {
|
||||
let Self {
|
||||
platform_output,
|
||||
needs_repaint,
|
||||
textures_delta,
|
||||
shapes,
|
||||
} = newer;
|
||||
|
||||
self.platform_output.append(platform_output);
|
||||
self.needs_repaint = needs_repaint; // if the last frame doesn't need a repaint, then we don't need to repaint
|
||||
self.textures_delta.append(textures_delta);
|
||||
self.shapes = shapes; // Only paint the latest
|
||||
}
|
||||
}
|
||||
|
||||
/// The non-rendering part of what egui emits each frame.
|
||||
///
|
||||
/// You can access (and modify) this with [`crate::Context::output`].
|
||||
///
|
||||
/// The backend should use this.
|
||||
#[derive(Clone, Default, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct Output {
|
||||
pub struct PlatformOutput {
|
||||
/// Set the cursor to this icon.
|
||||
pub cursor_icon: CursorIcon,
|
||||
|
||||
@@ -18,14 +63,6 @@ pub struct Output {
|
||||
/// This is often a response to [`crate::Event::Copy`] or [`crate::Event::Cut`].
|
||||
pub copied_text: String,
|
||||
|
||||
/// If `true`, egui is requesting immediate repaint (i.e. on the next frame).
|
||||
///
|
||||
/// This happens for instance when there is an animation, or if a user has called `Context::request_repaint()`.
|
||||
///
|
||||
/// As an egui user: don't set this value directly.
|
||||
/// Call `Context::request_repaint()` instead and it will do so for you.
|
||||
pub needs_repaint: bool,
|
||||
|
||||
/// Events that may be useful to e.g. a screen reader.
|
||||
pub events: Vec<OutputEvent>,
|
||||
|
||||
@@ -35,12 +72,9 @@ pub struct Output {
|
||||
|
||||
/// Screen-space position of text edit cursor (used for IME).
|
||||
pub text_cursor_pos: Option<crate::Pos2>,
|
||||
|
||||
/// Texture changes since last frame.
|
||||
pub textures_delta: epaint::textures::TexturesDelta,
|
||||
}
|
||||
|
||||
impl Output {
|
||||
impl PlatformOutput {
|
||||
/// Open the given url in a web browser.
|
||||
/// If egui is running in a browser, the same tab will be reused.
|
||||
pub fn open_url(&mut self, url: impl ToString) {
|
||||
@@ -70,11 +104,9 @@ impl Output {
|
||||
cursor_icon,
|
||||
open_url,
|
||||
copied_text,
|
||||
needs_repaint,
|
||||
mut events,
|
||||
mutable_text_under_cursor,
|
||||
text_cursor_pos,
|
||||
textures_delta,
|
||||
} = newer;
|
||||
|
||||
self.cursor_icon = cursor_icon;
|
||||
@@ -84,11 +116,9 @@ impl Output {
|
||||
if !copied_text.is_empty() {
|
||||
self.copied_text = copied_text;
|
||||
}
|
||||
self.needs_repaint = needs_repaint; // if the last frame doesn't need a repaint, then we don't need to repaint
|
||||
self.events.append(&mut events);
|
||||
self.mutable_text_under_cursor = mutable_text_under_cursor;
|
||||
self.text_cursor_pos = text_cursor_pos.or(self.text_cursor_pos);
|
||||
self.textures_delta.append(textures_delta);
|
||||
}
|
||||
|
||||
/// Take everything ephemeral (everything except `cursor_icon` currently)
|
||||
@@ -129,7 +159,7 @@ impl OpenUrl {
|
||||
|
||||
/// A mouse cursor icon.
|
||||
///
|
||||
/// egui emits a [`CursorIcon`] in [`Output`] each frame as a request to the integration.
|
||||
/// egui emits a [`CursorIcon`] in [`PlatformOutput`] each frame as a request to the integration.
|
||||
///
|
||||
/// Loosely based on <https://developer.mozilla.org/en-US/docs/Web/CSS/cursor>.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
|
||||
@@ -110,16 +110,16 @@
|
||||
//! To write your own integration for egui you need to do this:
|
||||
//!
|
||||
//! ``` no_run
|
||||
//! # fn handle_output(_: egui::Output) {}
|
||||
//! # fn paint(_: Vec<egui::ClippedMesh>) {}
|
||||
//! # fn handle_platform_output(_: egui::PlatformOutput) {}
|
||||
//! # fn gather_input() -> egui::RawInput { egui::RawInput::default() }
|
||||
//! # fn paint(textures_detla: egui::TexturesDelta, _: Vec<egui::ClippedMesh>) {}
|
||||
//! let mut ctx = egui::Context::default();
|
||||
//!
|
||||
//! // Game loop:
|
||||
//! loop {
|
||||
//! let raw_input: egui::RawInput = gather_input();
|
||||
//!
|
||||
//! let (output, shapes) = ctx.run(raw_input, |ctx| {
|
||||
//! let full_output = ctx.run(raw_input, |ctx| {
|
||||
//! egui::CentralPanel::default().show(&ctx, |ui| {
|
||||
//! ui.label("Hello world!");
|
||||
//! if ui.button("Click me").clicked() {
|
||||
@@ -127,10 +127,9 @@
|
||||
//! }
|
||||
//! });
|
||||
//! });
|
||||
//!
|
||||
//! let clipped_meshes = ctx.tessellate(shapes); // create triangles to paint
|
||||
//! handle_output(output);
|
||||
//! paint(clipped_meshes);
|
||||
//! handle_platform_output(full_output.platform_output);
|
||||
//! let clipped_meshes = ctx.tessellate(full_output.shapes); // create triangles to paint
|
||||
//! paint(full_output.textures_delta, clipped_meshes);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
@@ -403,7 +402,7 @@ pub use {
|
||||
context::Context,
|
||||
data::{
|
||||
input::*,
|
||||
output::{self, CursorIcon, Output, WidgetInfo},
|
||||
output::{self, CursorIcon, FullOutput, PlatformOutput, WidgetInfo},
|
||||
},
|
||||
grid::Grid,
|
||||
id::{Id, IdMap},
|
||||
|
||||
@@ -105,7 +105,7 @@ pub struct Options {
|
||||
pub tessellation_options: epaint::TessellationOptions,
|
||||
|
||||
/// This does not at all change the behavior of egui,
|
||||
/// but is a signal to any backend that we want the [`crate::Output::events`] read out loud.
|
||||
/// but is a signal to any backend that we want the [`crate::PlatformOutput::events`] read out loud.
|
||||
/// Screen readers is an experimental feature of egui, and not supported on all platforms.
|
||||
pub screen_reader: bool,
|
||||
|
||||
|
||||
@@ -366,10 +366,10 @@ impl Ui {
|
||||
self.ctx().data()
|
||||
}
|
||||
|
||||
/// The [`Output`] of the [`Context`] associated with this ui.
|
||||
/// The [`PlatformOutput`] of the [`Context`] associated with this ui.
|
||||
/// Equivalent to `.ctx().output()`.
|
||||
#[inline]
|
||||
pub fn output(&self) -> RwLockWriteGuard<'_, Output> {
|
||||
pub fn output(&self) -> RwLockWriteGuard<'_, PlatformOutput> {
|
||||
self.ctx().output()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user