From db87c712a1c02e6f1577c682b4f90fb926482364 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 13 Apr 2026 17:48:43 +0200 Subject: [PATCH 01/63] Replace `cargo machete` with `cargo shear` (#8094) We've had good experiences with `cargo shear` at Rerun --- .github/workflows/cargo_machete.yml | 19 -------------- .github/workflows/cargo_shear.yml | 25 +++++++++++++++++++ Cargo.lock | 2 -- crates/egui-winit/Cargo.toml | 3 +++ crates/egui_demo_app/Cargo.toml | 4 +-- crates/egui_demo_lib/Cargo.toml | 3 +++ crates/egui_glow/Cargo.toml | 4 --- examples/custom_style/Cargo.toml | 4 +-- examples/hello_world_par/Cargo.toml | 2 +- examples/images/Cargo.toml | 2 +- examples/puffin_profiler/Cargo.toml | 2 +- tests/egui_tests/Cargo.toml | 3 +++ tests/test_egui_extras_compilation/Cargo.toml | 2 +- 13 files changed, 42 insertions(+), 33 deletions(-) delete mode 100644 .github/workflows/cargo_machete.yml create mode 100644 .github/workflows/cargo_shear.yml diff --git a/.github/workflows/cargo_machete.yml b/.github/workflows/cargo_machete.yml deleted file mode 100644 index 1dc162e56..000000000 --- a/.github/workflows/cargo_machete.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Cargo Machete - -on: [push, pull_request] - -jobs: - cargo-machete: - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - uses: dtolnay/rust-toolchain@stable - with: - toolchain: 1.92 - - name: Machete install - ## The official cargo-machete action - uses: bnjbvr/cargo-machete@v0.9.1 - - name: Checkout - uses: actions/checkout@v4 - - name: Machete Check - run: cargo machete diff --git a/.github/workflows/cargo_shear.yml b/.github/workflows/cargo_shear.yml new file mode 100644 index 000000000..734abacb5 --- /dev/null +++ b/.github/workflows/cargo_shear.yml @@ -0,0 +1,25 @@ +# Looks for unused crates. +name: Cargo Shear + +on: + push: + branches: + - "main" + pull_request: + types: [opened, synchronize] + +jobs: + cargo-shear: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Cargo Shear + uses: taiki-e/install-action@v2.48.7 + with: + tool: cargo-shear@1.11.2 + + - name: Run Cargo Shear + run: | + cargo shear diff --git a/Cargo.lock b/Cargo.lock index 6efed68d0..caac14876 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1375,8 +1375,6 @@ dependencies = [ "log", "memoffset", "profiling", - "wasm-bindgen", - "web-sys", "winit", ] diff --git a/crates/egui-winit/Cargo.toml b/crates/egui-winit/Cargo.toml index dd4aa8f9d..7fea2341b 100644 --- a/crates/egui-winit/Cargo.toml +++ b/crates/egui-winit/Cargo.toml @@ -20,6 +20,9 @@ workspace = true all-features = true rustdoc-args = ["--generate-link-to-definition"] +[package.metadata.cargo-shear] +ignored = ["wayland-cursor"] # TODO(emilk): remove when we update winit + [features] default = ["clipboard", "links", "wayland", "winit/default", "x11"] diff --git a/crates/egui_demo_app/Cargo.toml b/crates/egui_demo_app/Cargo.toml index 5bb093ced..3b4de53e6 100644 --- a/crates/egui_demo_app/Cargo.toml +++ b/crates/egui_demo_app/Cargo.toml @@ -8,8 +8,8 @@ rust-version.workspace = true publish = false default-run = "egui_demo_app" -[package.metadata.cargo-machete] -ignored = ["profiling"] +[package.metadata.cargo-shear] +ignored = ["image", "profiling", "wasm-bindgen-futures"] [lints] workspace = true diff --git a/crates/egui_demo_lib/Cargo.toml b/crates/egui_demo_lib/Cargo.toml index dc57fb092..d2586fa11 100644 --- a/crates/egui_demo_lib/Cargo.toml +++ b/crates/egui_demo_lib/Cargo.toml @@ -20,6 +20,9 @@ workspace = true all-features = true rustdoc-args = ["--generate-link-to-definition"] +[package.metadata.cargo-shear] +ignored = ["image"] # We need the png feature + [lib] diff --git a/crates/egui_glow/Cargo.toml b/crates/egui_glow/Cargo.toml index c5ff714b3..d5a9f3716 100644 --- a/crates/egui_glow/Cargo.toml +++ b/crates/egui_glow/Cargo.toml @@ -66,10 +66,6 @@ document-features = { workspace = true, optional = true } # Native: winit = { workspace = true, optional = true, default-features = false, features = ["rwh_06"] } -# Web: -[target.'cfg(target_arch = "wasm32")'.dependencies] -web-sys = { workspace = true, features = ["console"] } -wasm-bindgen.workspace = true [dev-dependencies] glutin = { workspace = true, default-features = true } # examples/pure_glow diff --git a/examples/custom_style/Cargo.toml b/examples/custom_style/Cargo.toml index 83174df40..3352aa474 100644 --- a/examples/custom_style/Cargo.toml +++ b/examples/custom_style/Cargo.toml @@ -10,8 +10,8 @@ publish = false workspace = true -[package.metadata.cargo-machete] -ignored = ["image"] # We need the .png feature +[package.metadata.cargo-shear] +ignored = ["image"] # We need the png feature [dependencies] diff --git a/examples/hello_world_par/Cargo.toml b/examples/hello_world_par/Cargo.toml index ef719b742..04a3c0e25 100644 --- a/examples/hello_world_par/Cargo.toml +++ b/examples/hello_world_par/Cargo.toml @@ -11,7 +11,7 @@ publish = false workspace = true -[package.metadata.cargo-machete] +[package.metadata.cargo-shear] ignored = ["winit"] # Just enable some features of it; see below diff --git a/examples/images/Cargo.toml b/examples/images/Cargo.toml index 7c3b77b7c..adac2a540 100644 --- a/examples/images/Cargo.toml +++ b/examples/images/Cargo.toml @@ -11,7 +11,7 @@ publish = false workspace = true -[package.metadata.cargo-machete] +[package.metadata.cargo-shear] ignored = ["image"] # We only use the dependency to add more features to it diff --git a/examples/puffin_profiler/Cargo.toml b/examples/puffin_profiler/Cargo.toml index 49e6598db..ed8ce3aa0 100644 --- a/examples/puffin_profiler/Cargo.toml +++ b/examples/puffin_profiler/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" rust-version = "1.92" publish = false -[package.metadata.cargo-machete] +[package.metadata.cargo-shear] ignored = ["profiling"] [lints] diff --git a/tests/egui_tests/Cargo.toml b/tests/egui_tests/Cargo.toml index 8c09042b1..44a7b9c8f 100644 --- a/tests/egui_tests/Cargo.toml +++ b/tests/egui_tests/Cargo.toml @@ -5,6 +5,9 @@ license.workspace = true rust-version.workspace = true version.workspace = true +[package.metadata.cargo-shear] +ignored = ["image"] # We need the png feature + [dev-dependencies] egui = { workspace = true, default-features = true } egui_kittest = { workspace = true, features = ["snapshot", "wgpu"] } diff --git a/tests/test_egui_extras_compilation/Cargo.toml b/tests/test_egui_extras_compilation/Cargo.toml index 3598362a9..3e1d166f7 100644 --- a/tests/test_egui_extras_compilation/Cargo.toml +++ b/tests/test_egui_extras_compilation/Cargo.toml @@ -9,7 +9,7 @@ publish = false [lints] workspace = true -[package.metadata.cargo-machete] +[package.metadata.cargo-shear] ignored = ["eframe", "egui_extras"] # We don't use them, just check that things compile [dependencies] From 770090a6fff5500b22d73da1ee2d6da609d0e719 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 14 Apr 2026 10:52:28 +0200 Subject: [PATCH 02/63] Fix `egui_demo_app` on Linux (#8100) * Closes https://github.com/emilk/egui/issues/8098 --- crates/egui_demo_app/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/egui_demo_app/Cargo.toml b/crates/egui_demo_app/Cargo.toml index 3b4de53e6..f9a153268 100644 --- a/crates/egui_demo_app/Cargo.toml +++ b/crates/egui_demo_app/Cargo.toml @@ -23,7 +23,7 @@ crate-type = ["cdylib", "rlib"] [features] -default = ["wgpu", "persistence"] +default = ["wgpu", "persistence", "wayland", "x11"] web_app = ["http", "persistence"] From 152b97b434b6a2ff11b5d77b50a659ce1945e185 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 14 Apr 2026 11:35:52 +0200 Subject: [PATCH 03/63] Warn if using a software rasterizer (#8101) * Related to https://github.com/emilk/egui/issues/8093 --- crates/egui-wgpu/src/lib.rs | 41 +++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/crates/egui-wgpu/src/lib.rs b/crates/egui-wgpu/src/lib.rs index 180b305be..5ab91557b 100644 --- a/crates/egui-wgpu/src/lib.rs +++ b/crates/egui-wgpu/src/lib.rs @@ -138,29 +138,12 @@ async fn request_adapter( } })?; - if cfg!(target_arch = "wasm32") { - log::debug!( - "Picked wgpu adapter: {}", - adapter_info_summary(&adapter.get_info()) + if 1 < available_adapters.len() { + log::info!( + "There are {} available wgpu adapters: {}", + available_adapters.len(), + describe_adapters(available_adapters) ); - } else { - // native: - if available_adapters.len() == 1 { - log::debug!( - "Picked the only available wgpu adapter: {}", - adapter_info_summary(&adapter.get_info()) - ); - } else { - log::info!( - "There were {} available wgpu adapters: {}", - available_adapters.len(), - describe_adapters(available_adapters) - ); - log::debug!( - "Picked wgpu adapter: {}", - adapter_info_summary(&adapter.get_info()) - ); - } } Ok(adapter) @@ -236,6 +219,8 @@ impl RenderState { }) => (adapter, device, queue), }; + log_adapter_info(&adapter.get_info()); + let surface_formats = { profiling::scope!("get_capabilities"); compatible_surface.map_or_else( @@ -406,6 +391,18 @@ pub fn depth_format_from_bits(depth_buffer: u8, stencil_buffer: u8) -> Option String { let wgpu::AdapterInfo { From 5c96f4f080b9124e9a1bbb3c53c51b1ac272eeab Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 14 Apr 2026 13:00:35 +0200 Subject: [PATCH 04/63] Document glow-only fields in `NativeOptions` (#8104) --- crates/eframe/src/epi.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index b9a178a1d..bc19951bf 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -317,6 +317,8 @@ pub struct NativeOptions { /// Turn on vertical syncing, limiting the FPS to the display refresh rate. /// /// The default is `true`. + /// + /// Only affects the `glow` backend. pub vsync: bool, /// Set the level of the multisampling anti-aliasing (MSAA). @@ -343,6 +345,8 @@ pub struct NativeOptions { /// Specify whether or not hardware acceleration is preferred, required, or not. /// /// Default: [`HardwareAcceleration::Preferred`]. + /// + /// Only affects the `glow` backend. pub hardware_acceleration: HardwareAcceleration, /// What rendering backend to use. From 902906f9893a07f893ddc876c943febb8cd4da8d Mon Sep 17 00:00:00 2001 From: Lucas Meurer Date: Tue, 14 Apr 2026 13:13:59 +0200 Subject: [PATCH 05/63] Fix centered & right aligned `TextEdit` (#8082) A couple improvements to centered and right-aligned text edits: - Fix text selection in centered and right aligned text edits (ironically, this broke in #8076) - Fix cursor movement in centered and right aligned text edits (horizontal cursor position will be retained on vertical movement) - Multiline text edit exceeding available width if there are atoms - Added atoms & alignment options to text edit demo - Improve how vertical_align and horizontal_align are applied - Textedit atom is grow now, removing the need for the extra seperate grow atom - This allows us to apply the `align` on the text edit atom instead of the whole AtomLayout - Fixes https://github.com/emilk/egui/pull/8022 - Fixes https://github.com/emilk/egui/issues/7999 --- crates/egui/src/widgets/text_edit/builder.rs | 36 +++++++++------- crates/egui_demo_lib/src/demo/text_edit.rs | 42 ++++++++++++++++++- .../tests/snapshots/demos/TextEdit.png | 4 +- crates/epaint/src/text/text_layout_types.rs | 6 ++- .../layout/text_edit_prefix_suffix.png | 4 +- .../tests/snapshots/text_edit_halign.png | 4 +- .../visuals/text_edit_prefix_suffix.png | 4 +- 7 files changed, 74 insertions(+), 26 deletions(-) diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 8ba188443..416cd8c9b 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -4,8 +4,8 @@ use emath::{Rect, TSTransform}; use epaint::text::{Galley, LayoutJob, TextWrapMode, cursor::CCursor}; use crate::{ - Align, Align2, Atom, AtomExt as _, AtomKind, AtomLayout, Atoms, Color32, Context, CursorIcon, - Event, EventFilter, FontSelection, Frame, Id, ImeEvent, IntoAtoms, IntoSizedResult, Key, + Align, Align2, AtomExt as _, AtomKind, AtomLayout, Atoms, Color32, Context, CursorIcon, Event, + EventFilter, FontSelection, Frame, Id, ImeEvent, IntoAtoms, IntoSizedResult, Key, KeyboardShortcut, Margin, Modifiers, NumExt as _, Response, Sense, SizedAtomKind, TextBuffer, TextStyle, Ui, Vec2, Widget, WidgetInfo, WidgetWithState, epaint, os::OperatingSystem, @@ -480,11 +480,12 @@ impl TextEdit<'_> { let font_id_clone = font_id.clone(); let mut default_layouter = move |ui: &Ui, text: &dyn TextBuffer, wrap_width: f32| { let text = mask_if_password(password, text.as_str()); - let layout_job = if multiline { + let mut layout_job = if multiline { LayoutJob::simple(text, font_id_clone.clone(), text_color, wrap_width) } else { LayoutJob::simple_singleline(text, font_id_clone.clone(), text_color) }; + layout_job.halign = align.x(); ui.fonts_mut(|f| f.layout_job(layout_job)) }; @@ -591,6 +592,7 @@ impl TextEdit<'_> { if !shrunk && matches!(atom.kind, AtomKind::Text(_)) { // elide the hint_text if needed atom = atom.atom_shrink(true); + atom = atom.atom_grow(true); shrunk = true; } @@ -619,6 +621,11 @@ impl TextEdit<'_> { get_galley = Some(galley); } else { + // We need to shrink when clip_text, so that we don't exceed the available size + // and thus clip. We also need to shrink in multi line text edits, so text can + // wrap appropriately. + let should_shrink = clip_text || multiline; + // We need a closure here, so we can calculate the galley based on the available // width (after adding suffix and prefix), for correct wrapping in multi line text // edits @@ -645,16 +652,13 @@ impl TextEdit<'_> { sized: SizedAtomKind::Empty { size: Some(size) }, } }) + .atom_grow(true) + .atom_align(self.align) .atom_id(inner_rect_id) - .atom_shrink(clip_text), + .atom_shrink(should_shrink), ); } - // Ensure the suffix is always right-aligned - if !suffix.is_empty() { - atoms.push_right(Atom::grow()); - } - // TODO(servo/rust-smallvec#146): Use extend_right instead of the loop once we have // smallvec 2.0. Using `extend_right` here won't compile, due to lifetime issues. for atom in suffix { @@ -679,7 +683,7 @@ impl TextEdit<'_> { .max_width(allocate_width) .sense(sense) .frame(frame) - .align2(Align2::LEFT_TOP) + .align2(align) .wrap_mode(wrap_mode) .allocate(ui); @@ -740,16 +744,18 @@ impl TextEdit<'_> { // TODO(emilk): drag selected text to either move or clone (ctrl on windows, alt on mac) - let cursor_at_pointer = - galley.cursor_from_pos(pointer_pos - inner_rect.min + state.text_offset); + let cursor_at_pointer = galley.cursor_from_pos( + pointer_pos - inner_rect.min + state.text_offset + vec2(galley.rect.left(), 0.0), + ); if ui.visuals().text_cursor.preview && response.hovered() && ui.input(|i| i.pointer.is_moving()) { // text cursor preview: - let cursor_rect = TSTransform::from_translation(inner_rect.min.to_vec2()) - * cursor_rect(&galley, &cursor_at_pointer, row_height); + let cursor_rect = TSTransform::from_translation( + inner_rect.min.to_vec2() - vec2(galley.rect.left(), 0.0), + ) * cursor_rect(&galley, &cursor_at_pointer, row_height); text_selection::visuals::paint_cursor_end(&painter, ui.visuals(), cursor_rect); } @@ -835,7 +841,7 @@ impl TextEdit<'_> { if has_focus && let Some(cursor_range) = state.cursor.range(&galley) { let primary_cursor_rect = cursor_rect(&galley, &cursor_range.primary, row_height) - .translate(galley_pos.to_vec2()); + .translate(galley_pos.to_vec2() - vec2(galley.rect.left(), 0.0)); if response.changed() || selection_changed { // Scroll to keep primary cursor in view: diff --git a/crates/egui_demo_lib/src/demo/text_edit.rs b/crates/egui_demo_lib/src/demo/text_edit.rs index 3ec53a523..8fd42da11 100644 --- a/crates/egui_demo_lib/src/demo/text_edit.rs +++ b/crates/egui_demo_lib/src/demo/text_edit.rs @@ -1,15 +1,21 @@ +use egui::{Align, Align2, AtomExt as _}; + /// Showcase [`egui::TextEdit`]. #[derive(PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", serde(default))] pub struct TextEditDemo { pub text: String, + halign: egui::Align, + valign: egui::Align, } impl Default for TextEditDemo { fn default() -> Self { Self { text: "Edit this text".to_owned(), + halign: egui::Align::LEFT, + valign: egui::Align::TOP, } } } @@ -37,7 +43,11 @@ impl crate::View for TextEditDemo { ui.add(crate::egui_github_link_file!()); }); - let Self { text } = self; + let Self { + text, + halign, + valign, + } = self; ui.horizontal(|ui| { ui.spacing_mut().item_spacing.x = 0.0; @@ -46,10 +56,40 @@ impl crate::View for TextEditDemo { ui.label("."); }); + ui.horizontal(|ui| { + ui.label("Horizontal align:"); + ui.selectable_value(halign, egui::Align::LEFT, "Left"); + ui.selectable_value(halign, egui::Align::Center, "Center"); + ui.selectable_value(halign, egui::Align::RIGHT, "Right"); + }); + ui.horizontal(|ui| { + ui.label("Vertical align:"); + ui.selectable_value(valign, egui::Align::TOP, "Top"); + ui.selectable_value(valign, egui::Align::Center, "Center"); + ui.selectable_value(valign, egui::Align::BOTTOM, "Bottom"); + }); + + let clear_id = egui::Id::new("clear_button"); + let clear_size = egui::Vec2::splat(ui.spacing().interact_size.y); + let output = egui::TextEdit::multiline(text) .hint_text("Type something!") + // Atoms are centered by default, so we need to pass the right align here: + .prefix("πŸ”Ž".atom_align(Align2([Align::LEFT, *valign]))) + .suffix( + egui::Atom::custom(clear_id, clear_size) + .atom_align(Align2([Align::RIGHT, *valign])), + ) + .horizontal_align(*halign) + .vertical_align(*valign) .show(ui); + if let Some(rect) = output.response.rect(clear_id) + && ui.place(rect, egui::Button::new("❌")).clicked() + { + text.clear(); + } + ui.horizontal(|ui| { ui.spacing_mut().item_spacing.x = 0.0; ui.label("Selected text: "); diff --git a/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png b/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png index 81b49688b..839a15faa 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c09529c3a1c26c8f28c00fc15cc5f495842862276870c24b5ee0713954f97fc -size 21916 +oid sha256:94c4af5715992f4dbb5bbec6ce67eec1e2f66cfc078a3e704ec386bdb482cac4 +size 30064 diff --git a/crates/epaint/src/text/text_layout_types.rs b/crates/epaint/src/text/text_layout_types.rs index 481e3ebbf..fc987890d 100644 --- a/crates/epaint/src/text/text_layout_types.rs +++ b/crates/epaint/src/text/text_layout_types.rs @@ -1244,7 +1244,8 @@ impl Galley { let new_layout_cursor = { // keep same X coord - let column = self.rows[new_row].char_at(h_pos); + // char_at is Row-relative, so subtract the row's position + let column = self.rows[new_row].char_at(h_pos - self.rows[new_row].pos.x); LayoutCursor { row: new_row, column, @@ -1266,7 +1267,8 @@ impl Galley { let new_layout_cursor = { // keep same X coord - let column = self.rows[new_row].char_at(h_pos); + // char_at is Row-relative, so subtract the row's position + let column = self.rows[new_row].char_at(h_pos - self.rows[new_row].pos.x); LayoutCursor { row: new_row, column, diff --git a/tests/egui_tests/tests/snapshots/layout/text_edit_prefix_suffix.png b/tests/egui_tests/tests/snapshots/layout/text_edit_prefix_suffix.png index 439e89094..642a8c5fd 100644 --- a/tests/egui_tests/tests/snapshots/layout/text_edit_prefix_suffix.png +++ b/tests/egui_tests/tests/snapshots/layout/text_edit_prefix_suffix.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a40004fe56075f31162e16c7c59c00d7e1b8132bbea603b3c54c4dec0875b1bb -size 364491 +oid sha256:a9f298f8ea6692e7ccbddbe182a91824ce262913d50e9a7df104a6c63d39d8a0 +size 372564 diff --git a/tests/egui_tests/tests/snapshots/text_edit_halign.png b/tests/egui_tests/tests/snapshots/text_edit_halign.png index bda8964c8..2f81509e6 100644 --- a/tests/egui_tests/tests/snapshots/text_edit_halign.png +++ b/tests/egui_tests/tests/snapshots/text_edit_halign.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f567547c446ffa75f968e0ffc505560f3b3d4171319fbe59be27dde4e553e287 -size 13273 +oid sha256:48114ad6d116fb9288ce9fe3b173017bda317f69753e7ac03a090b8d02d6cb4d +size 13258 diff --git a/tests/egui_tests/tests/snapshots/visuals/text_edit_prefix_suffix.png b/tests/egui_tests/tests/snapshots/visuals/text_edit_prefix_suffix.png index c61f99ed0..e00868b1f 100644 --- a/tests/egui_tests/tests/snapshots/visuals/text_edit_prefix_suffix.png +++ b/tests/egui_tests/tests/snapshots/visuals/text_edit_prefix_suffix.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e11dbb1d48a3eadecb5c0e36917785fa1f107e4e283ff2f76831482fe7cd2042 -size 10051 +oid sha256:a37ed30425967301ffa7dda3fdc8f316dfd7f4665c731b17778e3e5942783e81 +size 10534 From 3607aae91d7bd5b3895cfa4a7e2056c41e0a5bfe Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 14 Apr 2026 13:14:16 +0200 Subject: [PATCH 06/63] Configure wgpu to be low-latency by default (#8103) This changes the default value of `WgpuConfiguration::desired_maximum_frame_latency` to `Some(1)`. For low-Hz displays, this results in significantly lower input latency. * Closes https://github.com/emilk/egui/issues/5037 ? * Related to https://github.com/emilk/egui/issues/7761 --- crates/egui-wgpu/src/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/egui-wgpu/src/lib.rs b/crates/egui-wgpu/src/lib.rs index 5ab91557b..75155899d 100644 --- a/crates/egui-wgpu/src/lib.rs +++ b/crates/egui-wgpu/src/lib.rs @@ -328,7 +328,12 @@ impl Default for WgpuConfiguration { fn default() -> Self { Self { present_mode: wgpu::PresentMode::AutoVsync, - desired_maximum_frame_latency: None, + desired_maximum_frame_latency: if cfg!(target_os = "ios") { + None // The default is good on iOS, while `Some(1)` cuts FPS in half + } else { + Some(1) // Low-latency by default. + }, + // No display handle available at this point β€” callers should replace this with // `WgpuSetup::from_display_handle(...)` before creating the instance if one is available. wgpu_setup: WgpuSetup::without_display_handle(), From fe5533e4501848430dfa77af723321b321af11d3 Mon Sep 17 00:00:00 2001 From: rustbasic <127506429+rustbasic@users.noreply.github.com> Date: Tue, 14 Apr 2026 20:49:54 +0900 Subject: [PATCH 07/63] Optimize text selection performance for large documents (#7917) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Perf: Optimize text selection and navigation performance for large documents** #### **Summary** This PR significantly improves the performance of text selection (double-clicking) and cursor navigation within `TextEdit` and `Label` widgets, particularly when handling large documents (e.g., 1MB+ or logs). It eliminates several $O(N^2)$ bottlenecks and unnecessary memory allocations in `text_cursor_state.rs`. #### **Problems Identified** 1. **$O(N^2)$ Word Boundary Scanning:** In `next_word_boundary_char_index`, `char_index_from_byte_index` was called repeatedly inside a loop. This caused the entire document to be scanned from the beginning for every word found, leading to quadratic time complexity. 2. **Heavy String Allocations:** `ccursor_previous_word` used `collect::()` and `rev()` to search backwards, causing a full copy and memory allocation of the text (or line) every time the user moved the cursor or double-clicked. 3. **Inefficient Line Start Finding:** `find_line_start` performed global character counts (`text.chars().count()`) and global skips, which is very slow for large files. 4. **Global Search Scope:** `select_word_at` was performing word boundary searches across the entire document even for simple double-click actions. #### **Key Changes & Optimizations** 1. **Line-Scoped Selection:** Updated `select_word_at` to first identify the current line and then perform word boundary searches within that local scope. This reduces the search space from millions of characters to hundreds. 2. **Linear Time ($O(N)$) Boundary Search:** Refactored `next_word_boundary_char_index` to use a running cumulative character counter. This ensures the text is scanned only once. 3. **Zero-Allocation Backwards Search:** Optimized `ccursor_previous_word` to use `next_back()` on the `DoubleEndedIterator` provided by `unicode-segmentation`. This removes all temporary `String` allocations. 4. **Byte-Based Line Search:** Optimized `find_line_start` to use byte-based reverse scanning (`rfind('\n')`), which is significantly faster than counting characters from the start of the document. #### **Performance Impact** In my tests with large text files (over 10,000 lines / 1MB+): - **Before:** Double-clicking a word caused a UI freeze for 2–5 seconds. - **After:** Word selection and navigation are near-instantaneous (0–1ms), providing a smooth "native-like" experience even in WASM environments. --- .../src/text_selection/text_cursor_state.rs | 136 +++++++++++------- 1 file changed, 85 insertions(+), 51 deletions(-) diff --git a/crates/egui/src/text_selection/text_cursor_state.rs b/crates/egui/src/text_selection/text_cursor_state.rs index 12e0656d8..ee7f6e512 100644 --- a/crates/egui/src/text_selection/text_cursor_state.rs +++ b/crates/egui/src/text_selection/text_cursor_state.rs @@ -106,38 +106,26 @@ impl TextCursorState { } fn select_word_at(text: &str, ccursor: CCursor) -> CCursorRange { - if ccursor.index == 0 { - CCursorRange::two(ccursor, ccursor_next_word(text, ccursor)) - } else { - let it = text.chars(); - let mut it = it.skip(ccursor.index - 1); - if let Some(char_before_cursor) = it.next() { - if let Some(char_after_cursor) = it.next() { - if is_word_char(char_before_cursor) && is_word_char(char_after_cursor) { - let min = ccursor_previous_word(text, ccursor + 1); - let max = ccursor_next_word(text, min); - CCursorRange::two(min, max) - } else if is_word_char(char_before_cursor) { - let min = ccursor_previous_word(text, ccursor); - let max = ccursor_next_word(text, min); - CCursorRange::two(min, max) - } else if is_word_char(char_after_cursor) { - let max = ccursor_next_word(text, ccursor); - CCursorRange::two(ccursor, max) - } else { - let min = ccursor_previous_word(text, ccursor); - let max = ccursor_next_word(text, ccursor); - CCursorRange::two(min, max) - } - } else { - let min = ccursor_previous_word(text, ccursor); - CCursorRange::two(min, ccursor) - } - } else { - let max = ccursor_next_word(text, ccursor); - CCursorRange::two(ccursor, max) - } + if text.is_empty() { + return CCursorRange::one(ccursor); } + + let line_start = find_line_start(text, ccursor); + let line_end = ccursor_next_line(text, line_start); + + let line_range = line_start.index..line_end.index; + let current_line_text = slice_char_range(text, line_range.clone()); + + let relative_idx = ccursor.index - line_start.index; + let relative_ccursor = CCursor::new(relative_idx); + + let min = ccursor_previous_word(current_line_text, relative_ccursor); + let max = ccursor_next_word(current_line_text, relative_ccursor); + + CCursorRange::two( + CCursor::new(line_start.index + min.index), + CCursor::new(line_start.index + max.index), + ) } fn select_line_at(text: &str, ccursor: CCursor) -> CCursorRange { @@ -209,16 +197,20 @@ fn ccursor_previous_line(text: &str, ccursor: CCursor) -> CCursor { } fn next_word_boundary_char_index(text: &str, cursor_ci: usize) -> usize { - for (word_byte_index, word) in text.split_word_bound_indices() { - let word_ci = char_index_from_byte_index(text, word_byte_index); + let mut current_char_idx = 0; + + for (_word_byte_index, word) in text.split_word_bound_indices() { + let word_ci = current_char_idx; // We consider `.` a word boundary. // At least that's how Mac works when navigating something like `www.example.com`. - for (dot_ci_offset, chr) in word.chars().enumerate() { - let dot_ci = word_ci + dot_ci_offset; + let mut word_char_count = 0; + for chr in word.chars() { + let dot_ci = word_ci + word_char_count; if chr == '.' && cursor_ci < dot_ci { return dot_ci; } + word_char_count += 1; } // Splitting considers contiguous whitespace as one word, such words must be skipped, @@ -228,9 +220,11 @@ fn next_word_boundary_char_index(text: &str, cursor_ci: usize) -> usize { if cursor_ci < word_ci && !all_word_chars(word) { return word_ci; } + + current_char_idx += word_char_count; } - char_index_from_byte_index(text, text.len()) + current_char_idx } fn all_word_chars(text: &str) -> bool { @@ -265,22 +259,14 @@ fn is_linebreak(c: char) -> bool { /// Accepts and returns character offset (NOT byte offset!). pub fn find_line_start(text: &str, current_index: CCursor) -> CCursor { - // We know that new lines, '\n', are a single byte char, but we have to - // work with char offsets because before the new line there may be any - // number of multi byte chars. - // We need to know the char index to be able to correctly set the cursor - // later. - let chars_count = text.chars().count(); + let byte_idx = byte_index_from_char_index(text, current_index.index); + let text_before = &text[..byte_idx]; - let position = text - .chars() - .rev() - .skip(chars_count - current_index.index) - .position(|x| x == '\n'); - - match position { - Some(pos) => CCursor::new(current_index.index - pos), - None => CCursor::new(0), + if let Some(last_newline_byte) = text_before.rfind('\n') { + let char_idx = char_index_from_byte_index(text, last_newline_byte + 1); + CCursor::new(char_idx) + } else { + CCursor::new(0) } } @@ -367,3 +353,51 @@ mod test { assert_eq!(next_word_boundary_char_index(text, 20), 21); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_previous_word_graphemes() { + let cases = [ + ("", 0, 0), + ("hello", 0, 0), + ("hello", "hello".chars().count(), 0), + ("hello world", 6, 0), + ("hello world", 8, 6), + ("hello world", "hello world".chars().count(), 6), + ("hello world ", "hello world ".chars().count(), 6), + ("hello world", "hello world".chars().count(), 8), + (" ", " ".chars().count(), 0), + ("hello, world", "hello, world".chars().count(), 7), + ("www.example.com", "www.example.com".chars().count(), 12), + ("μ•ˆλ…•! 😊 세상", 8, 6), + ("β€οΈπŸ‘ skvΔ›lΓ‘ knihovna πŸ‘β€οΈ", 18, 11), + ( + "a e\u{301} b", + "a e\u{301} b".chars().count(), + "a e\u{301} ".chars().count(), + ), + ( + "hi πŸ™‚ world", + "hi πŸ™‚ world".chars().count(), + "hi πŸ™‚ ".chars().count(), + ), + ( + "hi πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦ world", + "hi πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦ world".chars().count(), + "hi πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦ ".chars().count(), + ), + ]; + + for (text, cursor, expected) in cases { + let result = ccursor_previous_word(text, CCursor::new(cursor)); + assert_eq!( + result.index, expected, + "text={text:?}, cursor={cursor}, got={}, expected={expected}", + result.index + ); + } + } +} From 1cd89b5edcbaa3ab6080e9d00e760492a0d30be1 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 14 Apr 2026 20:19:36 +0200 Subject: [PATCH 08/63] Remove everything that was marked `#[deprecated]` (#8105) Simplify. Streamline. Spring cleaning. --- crates/eframe/src/epi.rs | 24 +- crates/eframe/src/lib.rs | 65 +- crates/eframe/src/native/epi_integration.rs | 8 +- crates/eframe/src/web/app_runner.rs | 3 - crates/egui/src/containers/area.rs | 4 +- .../egui/src/containers/collapsing_header.rs | 9 - crates/egui/src/containers/combo_box.rs | 6 - crates/egui/src/containers/frame.rs | 15 - crates/egui/src/containers/menu.rs | 5 +- crates/egui/src/containers/mod.rs | 2 - crates/egui/src/containers/old_popup.rs | 212 ----- crates/egui/src/containers/panel.rs | 226 +---- crates/egui/src/containers/popup.rs | 8 +- crates/egui/src/containers/resize.rs | 7 - crates/egui/src/containers/scroll_area.rs | 33 - crates/egui/src/containers/tooltip.rs | 18 - crates/egui/src/containers/window.rs | 6 +- crates/egui/src/context.rs | 230 +----- crates/egui/src/data/input.rs | 6 +- crates/egui/src/data/output.rs | 2 +- crates/egui/src/hit_test.rs | 2 +- crates/egui/src/input_state/mod.rs | 10 +- crates/egui/src/layers.rs | 8 +- crates/egui/src/lib.rs | 12 +- crates/egui/src/memory/mod.rs | 63 +- crates/egui/src/menu.rs | 781 ------------------ crates/egui/src/painter.rs | 41 - crates/egui/src/pass_state.rs | 93 +-- crates/egui/src/style.rs | 27 - .../egui/src/text_selection/cursor_range.rs | 6 - crates/egui/src/ui.rs | 276 +------ crates/egui/src/util/mod.rs | 4 - crates/egui/src/viewport.rs | 2 +- crates/egui/src/widget_text.rs | 4 +- crates/egui/src/widgets/button.rs | 6 - crates/egui/src/widgets/drag_value.rs | 16 - crates/egui/src/widgets/image.rs | 12 - crates/egui/src/widgets/image_button.rs | 164 ---- crates/egui/src/widgets/mod.rs | 28 +- crates/egui/src/widgets/progress_bar.rs | 6 - crates/egui/src/widgets/selected_label.rs | 13 - crates/egui/src/widgets/slider.rs | 12 +- crates/egui_extras/src/datepicker/button.rs | 8 - crates/egui_extras/src/table.rs | 11 +- crates/egui_kittest/src/app_kind.rs | 22 +- crates/egui_kittest/src/builder.rs | 63 -- crates/egui_kittest/src/lib.rs | 68 +- crates/egui_kittest/src/node.rs | 27 - crates/epaint/src/lib.rs | 6 - crates/epaint/src/margin_f32.rs | 15 - crates/epaint/src/shapes/shape.rs | 6 - crates/epaint/src/tessellator.rs | 10 - 52 files changed, 78 insertions(+), 2633 deletions(-) delete mode 100644 crates/egui/src/containers/old_popup.rs delete mode 100644 crates/egui/src/menu.rs delete mode 100644 crates/egui/src/widgets/image_button.rs delete mode 100644 crates/egui/src/widgets/selected_label.rs diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index bc19951bf..a9ce726fe 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -2,7 +2,7 @@ //! //! `epi` provides interfaces for window management and serialization. //! -//! Start by looking at the [`App`] trait, and implement [`App::update`]. +//! Start by looking at the [`App`] trait, and implement [`App::ui`]. #![warn(missing_docs)] // Let's keep `epi` well-documented. @@ -161,22 +161,6 @@ pub trait App { /// (A "viewport" in egui means an native OS window). fn ui(&mut self, ui: &mut egui::Ui, frame: &mut Frame); - /// Called each time the UI needs repainting, which may be many times per second. - /// - /// Put your widgets into a [`egui::Panel`], [`egui::CentralPanel`], [`egui::Window`] or [`egui::Area`]. - /// - /// The [`egui::Context`] can be cloned and saved if you like. - /// - /// To force a repaint, call [`egui::Context::request_repaint`] at any time (e.g. from another thread). - /// - /// This is called for the root viewport ([`egui::ViewportId::ROOT`]). - /// Use [`egui::Context::show_viewport_deferred`] to spawn additional viewports (windows). - /// (A "viewport" in egui means an native OS window). - #[deprecated = "Use Self::ui instead"] - fn update(&mut self, ctx: &egui::Context, frame: &mut Frame) { - _ = (ctx, frame); - } - /// Get a handle to the app. /// /// Can be used from web to interact or other external context. @@ -256,7 +240,7 @@ pub trait App { true } - /// A hook for manipulating or filtering raw input before it is processed by [`Self::update`]. + /// A hook for manipulating or filtering raw input before it is processed by [`Self::ui`]. /// /// This function provides a way to modify or filter input events before they are processed by egui. /// @@ -780,7 +764,7 @@ impl Frame { /// * Read the pixel buffer from the previous frame (`glow::Context::read_pixels`). /// * Render things behind the egui windows. /// - /// Note that all egui painting is deferred to after the call to [`App::update`] + /// Note that all egui painting is deferred to after the call to [`App::ui`] /// ([`egui`] only collects [`egui::Shape`]s and then eframe paints them all in one go later on). /// /// To get a [`glow`] context you need to compile with the `glow` feature flag, @@ -886,7 +870,7 @@ pub struct IntegrationInfo { /// Seconds of cpu usage (in seconds) on the previous frame. /// - /// This includes [`App::update`] as well as rendering (except for vsync waiting). + /// This includes [`App::ui`] as well as rendering (except for vsync waiting). /// /// For a more detailed view of cpu usage, connect your preferred profiler by enabling it's feature in [`profiling`](https://crates.io/crates/profiling). /// diff --git a/crates/eframe/src/lib.rs b/crates/eframe/src/lib.rs index c644289e0..9f7a4f3ef 100644 --- a/crates/eframe/src/lib.rs +++ b/crates/eframe/src/lib.rs @@ -6,7 +6,7 @@ //! To get started, see the [examples](https://github.com/emilk/egui/tree/main/examples). //! To learn how to set up `eframe` for web and native, go to and follow the instructions there! //! -//! In short, you implement [`App`] (especially [`App::update`]) and then +//! In short, you implement [`App`] (especially [`App::ui`]) and then //! call [`crate::run_native`] from your `main.rs`, and/or use `eframe::WebRunner` from your `lib.rs`. //! //! ## Compiling for web @@ -19,7 +19,7 @@ //! //! ## Simplified usage //! If your app is only for native, and you don't need advanced features like state persistence, -//! then you can use the simpler function [`run_simple_native`]. +//! then you can use the simpler function [`run_ui_native`]. //! //! ## Usage, native: //! ``` no_run @@ -446,67 +446,6 @@ pub fn run_ui_native( ) } -/// The simplest way to get started when writing a native app. -/// -/// This does NOT support persistence of custom user data. For that you need to use [`run_native`]. -/// However, it DOES support persistence of egui data (window positions and sizes, how far the user has scrolled in a -/// [`ScrollArea`](egui::ScrollArea), etc.) if the persistence feature is enabled. -/// -/// # Example -/// ``` no_run -/// fn main() -> eframe::Result { -/// // Our application state: -/// let mut name = "Arthur".to_owned(); -/// let mut age = 42; -/// -/// let options = eframe::NativeOptions::default(); -/// eframe::run_simple_native("My egui App", options, move |ctx, _frame| { -/// egui::CentralPanel::default().show(ctx, |ui| { -/// ui.heading("My egui Application"); -/// ui.horizontal(|ui| { -/// let name_label = ui.label("Your name: "); -/// ui.text_edit_singleline(&mut name) -/// .labelled_by(name_label.id); -/// }); -/// ui.add(egui::Slider::new(&mut age, 0..=120).text("age")); -/// if ui.button("Increment").clicked() { -/// age += 1; -/// } -/// ui.label(format!("Hello '{name}', age {age}")); -/// }); -/// }) -/// } -/// ``` -/// -/// # Errors -/// This function can fail if we fail to set up a graphics context. -#[deprecated = "Use run_ui_native instead"] -#[cfg(not(target_arch = "wasm32"))] -#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))] -pub fn run_simple_native( - app_name: &str, - native_options: NativeOptions, - update_fun: impl FnMut(&egui::Context, &mut Frame) + 'static, -) -> Result { - struct SimpleApp { - update_fun: U, - } - - impl App for SimpleApp { - fn ui(&mut self, _ui: &mut egui::Ui, _frame: &mut Frame) {} - - fn update(&mut self, ctx: &egui::Context, frame: &mut Frame) { - (self.update_fun)(ctx, frame); - } - } - - run_native( - app_name, - native_options, - Box::new(|_cc| Ok(Box::new(SimpleApp { update_fun }))), - ) -} - // ---------------------------------------------------------------------------- /// The different problems that can occur when trying to run `eframe`. diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index 5b22eb08c..e558b0a2b 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -259,7 +259,7 @@ impl EpiIntegration { /// Run user code - this can create immediate viewports, so hold no locks over this! /// - /// If `viewport_ui_cb` is None, we are in the root viewport and will call [`crate::App::update`]. + /// If `viewport_ui_cb` is None, we are in the root viewport and will call [`crate::App::ui`]. pub fn update( &mut self, app: &mut dyn epi::App, @@ -287,12 +287,6 @@ impl EpiIntegration { } if is_visible { - { - profiling::scope!("App::update"); - #[expect(deprecated)] - app.update(ui.ctx(), &mut self.frame); - } - { profiling::scope!("App::ui"); app.ui(ui, &mut self.frame); diff --git a/crates/eframe/src/web/app_runner.rs b/crates/eframe/src/web/app_runner.rs index 7161a665e..c15e78d68 100644 --- a/crates/eframe/src/web/app_runner.rs +++ b/crates/eframe/src/web/app_runner.rs @@ -284,9 +284,6 @@ impl AppRunner { self.app.logic(ui.ctx(), &mut self.frame); if is_visible { - #[expect(deprecated)] - self.app.update(ui.ctx(), &mut self.frame); - self.app.ui(ui, &mut self.frame); } }); diff --git a/crates/egui/src/containers/area.rs b/crates/egui/src/containers/area.rs index d44c0ae41..09488058d 100644 --- a/crates/egui/src/containers/area.rs +++ b/crates/egui/src/containers/area.rs @@ -280,7 +280,7 @@ impl Area { self } - /// Constrains this area to [`Context::screen_rect`]? + /// Constrains this area to [`Context::content_rect`]? /// /// Default: `true`. #[inline] @@ -291,7 +291,7 @@ impl Area { /// Constrain the movement of the window to the given rectangle. /// - /// For instance: `.constrain_to(ctx.screen_rect())`. + /// For instance: `.constrain_to(ctx.content_rect())`. #[inline] pub fn constrain_to(mut self, constrain_rect: Rect) -> Self { self.constrain = true; diff --git a/crates/egui/src/containers/collapsing_header.rs b/crates/egui/src/containers/collapsing_header.rs index aca8ab138..de3581288 100644 --- a/crates/egui/src/containers/collapsing_header.rs +++ b/crates/egui/src/containers/collapsing_header.rs @@ -451,15 +451,6 @@ impl CollapsingHeader { self } - /// Explicitly set the source of the [`Id`] of this widget, instead of using title label. - /// This is useful if the title label is dynamic or not unique. - #[deprecated = "Renamed id_salt"] - #[inline] - pub fn id_source(mut self, id_salt: impl Hash) -> Self { - self.id_salt = Id::new(id_salt); - self - } - /// If you set this to `false`, the [`CollapsingHeader`] will be grayed out and un-clickable. /// /// This is a convenience for [`Ui::disable`]. diff --git a/crates/egui/src/containers/combo_box.rs b/crates/egui/src/containers/combo_box.rs index c4097f803..adbda583c 100644 --- a/crates/egui/src/containers/combo_box.rs +++ b/crates/egui/src/containers/combo_box.rs @@ -94,12 +94,6 @@ impl ComboBox { } } - /// Without label. - #[deprecated = "Renamed from_id_salt"] - pub fn from_id_source(id_salt: impl std::hash::Hash) -> Self { - Self::from_id_salt(id_salt) - } - /// Set the outer width of the button and menu. /// /// Default is [`Spacing::combo_width`]. diff --git a/crates/egui/src/containers/frame.rs b/crates/egui/src/containers/frame.rs index d556f827e..5bbce626d 100644 --- a/crates/egui/src/containers/frame.rs +++ b/crates/egui/src/containers/frame.rs @@ -174,11 +174,6 @@ impl Frame { Self::NONE } - #[deprecated = "Use `Frame::NONE` or `Frame::new()` instead."] - pub const fn none() -> Self { - Self::NONE - } - /// For when you want to group a few widgets together within a frame. pub fn group(style: &Style) -> Self { Self::new() @@ -283,16 +278,6 @@ impl Frame { self } - /// The rounding of the _outer_ corner of the [`Self::stroke`] - /// (or, if there is no stroke, the outer corner of [`Self::fill`]). - /// - /// In other words, this is the corner radius of the _widget rect_. - #[inline] - #[deprecated = "Renamed to `corner_radius`"] - pub fn rounding(self, corner_radius: impl Into) -> Self { - self.corner_radius(corner_radius) - } - /// Margin outside the painted frame. /// /// Similar to what is called `margin` in CSS. diff --git a/crates/egui/src/containers/menu.rs b/crates/egui/src/containers/menu.rs index cfdaac827..d4c95b298 100644 --- a/crates/egui/src/containers/menu.rs +++ b/crates/egui/src/containers/menu.rs @@ -197,7 +197,7 @@ impl MenuState { /// Horizontal menu bar where you can add [`MenuButton`]s. /// -/// The menu bar goes well in a [`crate::TopBottomPanel::top`], +/// The menu bar goes well in a [`crate::Panel::top`], /// but can also be placed in a [`crate::Window`]. /// In the latter case you may want to wrap it in [`Frame`]. /// @@ -219,9 +219,6 @@ pub struct MenuBar { style: StyleModifier, } -#[deprecated = "Renamed to `egui::MenuBar`"] -pub type Bar = MenuBar; - impl Default for MenuBar { fn default() -> Self { Self { diff --git a/crates/egui/src/containers/mod.rs b/crates/egui/src/containers/mod.rs index a8f3306e9..d828ce0f8 100644 --- a/crates/egui/src/containers/mod.rs +++ b/crates/egui/src/containers/mod.rs @@ -9,7 +9,6 @@ mod combo_box; pub mod frame; pub mod menu; pub mod modal; -pub mod old_popup; pub mod panel; mod popup; pub(crate) mod resize; @@ -26,7 +25,6 @@ pub use { combo_box::*, frame::Frame, modal::{Modal, ModalResponse}, - old_popup::*, panel::*, popup::*, resize::Resize, diff --git a/crates/egui/src/containers/old_popup.rs b/crates/egui/src/containers/old_popup.rs deleted file mode 100644 index f8e7cc900..000000000 --- a/crates/egui/src/containers/old_popup.rs +++ /dev/null @@ -1,212 +0,0 @@ -//! Old and deprecated API for popups. Use [`Popup`] instead. -#![expect(deprecated)] - -use crate::containers::tooltip::Tooltip; -use crate::{ - Align, Context, Id, LayerId, Layout, Popup, PopupAnchor, PopupCloseBehavior, Pos2, Rect, - Response, Ui, Widget as _, WidgetText, -}; -use emath::RectAlign; -// ---------------------------------------------------------------------------- - -/// Show a tooltip at the current pointer position (if any). -/// -/// Most of the time it is easier to use [`Response::on_hover_ui`]. -/// -/// See also [`show_tooltip_text`]. -/// -/// Returns `None` if the tooltip could not be placed. -/// -/// ``` -/// # egui::__run_test_ui(|ui| { -/// # #[expect(deprecated)] -/// if ui.ui_contains_pointer() { -/// egui::show_tooltip(ui.ctx(), ui.layer_id(), egui::Id::new("my_tooltip"), |ui| { -/// ui.label("Helpful text"); -/// }); -/// } -/// # }); -/// ``` -#[deprecated = "Use `egui::Tooltip` instead"] -pub fn show_tooltip( - ctx: &Context, - parent_layer: LayerId, - widget_id: Id, - add_contents: impl FnOnce(&mut Ui) -> R, -) -> Option { - show_tooltip_at_pointer(ctx, parent_layer, widget_id, add_contents) -} - -/// Show a tooltip at the current pointer position (if any). -/// -/// Most of the time it is easier to use [`Response::on_hover_ui`]. -/// -/// See also [`show_tooltip_text`]. -/// -/// Returns `None` if the tooltip could not be placed. -/// -/// ``` -/// # egui::__run_test_ui(|ui| { -/// if ui.ui_contains_pointer() { -/// egui::show_tooltip_at_pointer(ui.ctx(), ui.layer_id(), egui::Id::new("my_tooltip"), |ui| { -/// ui.label("Helpful text"); -/// }); -/// } -/// # }); -/// ``` -#[deprecated = "Use `egui::Tooltip` instead"] -pub fn show_tooltip_at_pointer( - ctx: &Context, - parent_layer: LayerId, - widget_id: Id, - add_contents: impl FnOnce(&mut Ui) -> R, -) -> Option { - Tooltip::always_open(ctx.clone(), parent_layer, widget_id, PopupAnchor::Pointer) - .gap(12.0) - .show(add_contents) - .map(|response| response.inner) -} - -/// Show a tooltip under the given area. -/// -/// If the tooltip does not fit under the area, it tries to place it above it instead. -#[deprecated = "Use `egui::Tooltip` instead"] -pub fn show_tooltip_for( - ctx: &Context, - parent_layer: LayerId, - widget_id: Id, - widget_rect: &Rect, - add_contents: impl FnOnce(&mut Ui) -> R, -) -> Option { - Tooltip::always_open(ctx.clone(), parent_layer, widget_id, *widget_rect) - .show(add_contents) - .map(|response| response.inner) -} - -/// Show a tooltip at the given position. -/// -/// Returns `None` if the tooltip could not be placed. -#[deprecated = "Use `egui::Tooltip` instead"] -pub fn show_tooltip_at( - ctx: &Context, - parent_layer: LayerId, - widget_id: Id, - suggested_position: Pos2, - add_contents: impl FnOnce(&mut Ui) -> R, -) -> Option { - Tooltip::always_open(ctx.clone(), parent_layer, widget_id, suggested_position) - .show(add_contents) - .map(|response| response.inner) -} - -/// Show some text at the current pointer position (if any). -/// -/// Most of the time it is easier to use [`Response::on_hover_text`]. -/// -/// See also [`show_tooltip`]. -/// -/// Returns `None` if the tooltip could not be placed. -/// -/// ``` -/// # egui::__run_test_ui(|ui| { -/// if ui.ui_contains_pointer() { -/// egui::show_tooltip_text(ui.ctx(), ui.layer_id(), egui::Id::new("my_tooltip"), "Helpful text"); -/// } -/// # }); -/// ``` -#[deprecated = "Use `egui::Tooltip` instead"] -pub fn show_tooltip_text( - ctx: &Context, - parent_layer: LayerId, - widget_id: Id, - text: impl Into, -) -> Option<()> { - show_tooltip(ctx, parent_layer, widget_id, |ui| { - crate::widgets::Label::new(text).ui(ui); - }) -} - -/// Was this tooltip visible last frame? -#[deprecated = "Use `Tooltip::was_tooltip_open_last_frame` instead"] -pub fn was_tooltip_open_last_frame(ctx: &Context, widget_id: Id) -> bool { - Tooltip::was_tooltip_open_last_frame(ctx, widget_id) -} - -/// Indicate whether a popup will be shown above or below the box. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum AboveOrBelow { - Above, - Below, -} - -/// Helper for [`popup_above_or_below_widget`]. -#[deprecated = "Use `egui::Popup` instead"] -pub fn popup_below_widget( - ui: &Ui, - popup_id: Id, - widget_response: &Response, - close_behavior: PopupCloseBehavior, - add_contents: impl FnOnce(&mut Ui) -> R, -) -> Option { - popup_above_or_below_widget( - ui, - popup_id, - widget_response, - AboveOrBelow::Below, - close_behavior, - add_contents, - ) -} - -/// Shows a popup above or below another widget. -/// -/// Useful for drop-down menus (combo boxes) or suggestion menus under text fields. -/// -/// The opened popup will have a minimum width matching its parent. -/// -/// You must open the popup with [`crate::Memory::open_popup`] or [`crate::Memory::toggle_popup`]. -/// -/// Returns `None` if the popup is not open. -/// -/// ``` -/// # egui::__run_test_ui(|ui| { -/// let response = ui.button("Open popup"); -/// let popup_id = ui.make_persistent_id("my_unique_id"); -/// if response.clicked() { -/// ui.memory_mut(|mem| mem.toggle_popup(popup_id)); -/// } -/// let below = egui::AboveOrBelow::Below; -/// let close_on_click_outside = egui::PopupCloseBehavior::CloseOnClickOutside; -/// # #[expect(deprecated)] -/// egui::popup_above_or_below_widget(ui, popup_id, &response, below, close_on_click_outside, |ui| { -/// ui.set_min_width(200.0); // if you want to control the size -/// ui.label("Some more info, or things you can select:"); -/// ui.label("…"); -/// }); -/// # }); -/// ``` -#[deprecated = "Use `egui::Popup` instead"] -pub fn popup_above_or_below_widget( - _parent_ui: &Ui, - popup_id: Id, - widget_response: &Response, - above_or_below: AboveOrBelow, - close_behavior: PopupCloseBehavior, - add_contents: impl FnOnce(&mut Ui) -> R, -) -> Option { - let response = Popup::from_response(widget_response) - .layout(Layout::top_down_justified(Align::LEFT)) - .open_memory(None) - .close_behavior(close_behavior) - .id(popup_id) - .align(match above_or_below { - AboveOrBelow::Above => RectAlign::TOP_START, - AboveOrBelow::Below => RectAlign::BOTTOM_START, - }) - .width(widget_response.rect.width()) - .show(|ui| { - ui.set_min_width(ui.available_width()); - add_contents(ui) - })?; - Some(response.inner) -} diff --git a/crates/egui/src/containers/panel.rs b/crates/egui/src/containers/panel.rs index 4a83ce8d1..d75a90bf4 100644 --- a/crates/egui/src/containers/panel.rs +++ b/crates/egui/src/containers/panel.rs @@ -18,9 +18,8 @@ use emath::{GuiRounding as _, Pos2}; use crate::{ - Align, Context, CursorIcon, Frame, Id, InnerResponse, LayerId, Layout, NumExt as _, Rangef, - Rect, Sense, Stroke, Ui, UiBuilder, UiKind, UiStackInfo, Vec2, WidgetInfo, WidgetType, lerp, - vec2, + Align, Context, CursorIcon, Frame, Id, InnerResponse, Layout, NumExt as _, Rangef, Rect, Sense, + Stroke, Ui, UiBuilder, UiKind, UiStackInfo, Vec2, lerp, vec2, }; fn animate_expansion(ctx: &Context, id: Id, is_expanded: bool) -> f32 { @@ -451,59 +450,6 @@ impl Panel { } } -// Deprecated -impl Panel { - #[deprecated = "Renamed default_size"] - pub fn default_width(self, default_size: f32) -> Self { - self.default_size(default_size) - } - - #[deprecated = "Renamed min_size"] - pub fn min_width(self, min_size: f32) -> Self { - self.min_size(min_size) - } - - #[deprecated = "Renamed max_size"] - pub fn max_width(self, max_size: f32) -> Self { - self.max_size(max_size) - } - - #[deprecated = "Renamed size_range"] - pub fn width_range(self, size_range: impl Into) -> Self { - self.size_range(size_range) - } - - #[deprecated = "Renamed exact_size"] - pub fn exact_width(self, size: f32) -> Self { - self.exact_size(size) - } - - #[deprecated = "Renamed default_size"] - pub fn default_height(self, default_size: f32) -> Self { - self.default_size(default_size) - } - - #[deprecated = "Renamed min_size"] - pub fn min_height(self, min_size: f32) -> Self { - self.min_size(min_size) - } - - #[deprecated = "Renamed max_size"] - pub fn max_height(self, max_size: f32) -> Self { - self.max_size(max_size) - } - - #[deprecated = "Renamed size_range"] - pub fn height_range(self, size_range: impl Into) -> Self { - self.size_range(size_range) - } - - #[deprecated = "Renamed exact_size"] - pub fn exact_height(self, size: f32) -> Self { - self.exact_size(size) - } -} - // Public showing methods impl Panel { /// Show the panel inside a [`Ui`]. @@ -515,41 +461,6 @@ impl Panel { self.show_inside_dyn(ui, Box::new(add_contents)) } - /// Show the panel at the top level. - #[deprecated = "Use show_inside() instead"] - pub fn show( - self, - ctx: &Context, - add_contents: impl FnOnce(&mut Ui) -> R, - ) -> InnerResponse { - self.show_dyn(ctx, Box::new(add_contents)) - } - - /// Show the panel if `is_expanded` is `true`, - /// otherwise don't show it, but with a nice animation between collapsed and expanded. - #[deprecated = "Use show_animated_inside() instead"] - pub fn show_animated( - self, - ctx: &Context, - is_expanded: bool, - add_contents: impl FnOnce(&mut Ui) -> R, - ) -> Option> { - #![expect(deprecated)] - - let how_expanded = animate_expansion(ctx, self.id.with("animation"), is_expanded); - - let animated_panel = self.get_animated_panel(ctx, is_expanded)?; - - if how_expanded < 1.0 { - // Show a fake panel in this in-between animation state: - animated_panel.show(ctx, |_ui| {}); - None - } else { - // Show the real panel: - Some(animated_panel.show(ctx, add_contents)) - } - } - /// Show the panel if `is_expanded` is `true`, /// otherwise don't show it, but with a nice animation between collapsed and expanded. pub fn show_animated_inside( @@ -577,34 +488,6 @@ impl Panel { } } - /// Show either a collapsed or a expanded panel, with a nice animation between. - #[deprecated = "Use show_animated_between_inside() instead"] - pub fn show_animated_between( - ctx: &Context, - is_expanded: bool, - collapsed_panel: Self, - expanded_panel: Self, - add_contents: impl FnOnce(&mut Ui, f32) -> R, - ) -> Option> { - #![expect(deprecated)] - - let how_expanded = animate_expansion(ctx, expanded_panel.id.with("animation"), is_expanded); - - // Get either the fake or the real panel to animate - let animated_between_panel = - Self::get_animated_between_panel(ctx, is_expanded, collapsed_panel, expanded_panel); - - if 0.0 == how_expanded { - Some(animated_between_panel.show(ctx, |ui| add_contents(ui, how_expanded))) - } else if how_expanded < 1.0 { - // Show animation: - animated_between_panel.show(ctx, |ui| add_contents(ui, how_expanded)); - None - } else { - Some(animated_between_panel.show(ctx, |ui| add_contents(ui, how_expanded))) - } - } - /// Show either a collapsed or a expanded panel, with a nice animation between. pub fn show_animated_between_inside( ui: &mut Ui, @@ -756,59 +639,6 @@ impl Panel { inner_response } - /// Show the panel at the top level. - fn show_dyn<'c, R>( - self, - ctx: &Context, - add_contents: Box R + 'c>, - ) -> InnerResponse { - #![expect(deprecated)] - - let side = self.side; - let available_rect = ctx.available_rect(); - let mut panel_ui = Ui::new( - ctx.clone(), - self.id, - UiBuilder::new() - .layer_id(LayerId::background()) - .max_rect(available_rect), - ); - panel_ui.set_clip_rect(ctx.content_rect()); - panel_ui - .response() - .widget_info(|| WidgetInfo::new(WidgetType::Panel)); - - let inner_response = self.show_inside_dyn(&mut panel_ui, add_contents); - let rect = inner_response.response.rect; - - match side { - PanelSide::Vertical(side) => match side { - VerticalSide::Left => ctx.pass_state_mut(|state| { - state.allocate_left_panel(Rect::from_min_max(available_rect.min, rect.max)); - }), - VerticalSide::Right => ctx.pass_state_mut(|state| { - state.allocate_right_panel(Rect::from_min_max(rect.min, available_rect.max)); - }), - }, - PanelSide::Horizontal(side) => match side { - HorizontalSide::Top => { - ctx.pass_state_mut(|state| { - state.allocate_top_panel(Rect::from_min_max(available_rect.min, rect.max)); - }); - } - HorizontalSide::Bottom => { - ctx.pass_state_mut(|state| { - state.allocate_bottom_panel(Rect::from_min_max( - rect.min, - available_rect.max, - )); - }); - } - }, - } - inner_response - } - fn prepare_resizable_panel(&self, panel_sizer: &mut PanelSizer<'_>, ui: &Ui) { let resize_id = self.id.with("__resize"); let resize_response = ui.ctx().read_response(resize_id); @@ -1044,61 +874,9 @@ impl CentralPanel { response } - - /// Show the panel at the top level. - #[deprecated = "Use show_inside() instead"] - pub fn show( - self, - ctx: &Context, - add_contents: impl FnOnce(&mut Ui) -> R, - ) -> InnerResponse { - self.show_dyn(ctx, Box::new(add_contents)) - } - - /// Show the panel at the top level. - fn show_dyn<'c, R>( - self, - ctx: &Context, - add_contents: Box R + 'c>, - ) -> InnerResponse { - #![expect(deprecated)] - - let id = Id::new((ctx.viewport_id(), "central_panel")); - - let mut panel_ui = Ui::new( - ctx.clone(), - id, - UiBuilder::new() - .layer_id(LayerId::background()) - .max_rect(ctx.available_rect()), - ); - panel_ui.set_clip_rect(ctx.content_rect()); - - if false { - // TODO(emilk): @lucasmerlin shouldn't we enable this? - panel_ui - .response() - .widget_info(|| WidgetInfo::new(WidgetType::Panel)); - } - - let inner_response = self.show_inside_dyn(&mut panel_ui, add_contents); - - // Only inform ctx about what we actually used, so we can shrink the native window to fit. - ctx.pass_state_mut(|state| state.allocate_central_panel(inner_response.response.rect)); - - inner_response - } } fn clamp_to_range(x: f32, range: Rangef) -> f32 { let range = range.as_positive(); x.clamp(range.min, range.max) } - -// ---------------------------------------------------------------------------- - -#[deprecated = "Use Panel::left or Panel::right instead"] -pub type SidePanel = super::Panel; - -#[deprecated = "Use Panel::top or Panel::bottom instead"] -pub type TopBottomPanel = super::Panel; diff --git a/crates/egui/src/containers/popup.rs b/crates/egui/src/containers/popup.rs index 0fb2a9f2a..cf78a6650 100644 --- a/crates/egui/src/containers/popup.rs +++ b/crates/egui/src/containers/popup.rs @@ -1,5 +1,3 @@ -#![expect(deprecated)] // This is a new, safe wrapper around the old `Memory::popup` API. - use std::iter::once; use emath::{Align, Pos2, Rect, RectAlign, Vec2, vec2}; @@ -87,7 +85,7 @@ pub enum PopupCloseBehavior { /// but in the popup's body CloseOnClickOutside, - /// Clicks will be ignored. Popup might be closed manually by calling [`crate::Memory::close_all_popups`] + /// Clicks will be ignored. Popup might be closed manually by calling [`Popup::close_all`] /// or by pressing the escape button IgnoreClicks, } @@ -666,10 +664,6 @@ impl Popup<'_> { } /// Open the given popup and close all others. - /// - /// If you are NOT using [`Popup::show`], you must - /// also call [`crate::Memory::keep_popup_open`] as long as - /// you're showing the popup. pub fn open_id(ctx: &Context, popup_id: Id) { ctx.memory_mut(|mem| mem.open_popup(popup_id)); } diff --git a/crates/egui/src/containers/resize.rs b/crates/egui/src/containers/resize.rs index 7ff943b3f..8dcce6b20 100644 --- a/crates/egui/src/containers/resize.rs +++ b/crates/egui/src/containers/resize.rs @@ -69,13 +69,6 @@ impl Resize { self } - /// A source for the unique [`Id`], e.g. `.id_source("second_resize_area")` or `.id_source(loop_index)`. - #[inline] - #[deprecated = "Renamed id_salt"] - pub fn id_source(self, id_salt: impl std::hash::Hash) -> Self { - self.id_salt(id_salt) - } - /// A source for the unique [`Id`], e.g. `.id_salt("second_resize_area")` or `.id_salt(loop_index)`. #[inline] pub fn id_salt(mut self, id_salt: impl std::hash::Hash) -> Self { diff --git a/crates/egui/src/containers/scroll_area.rs b/crates/egui/src/containers/scroll_area.rs index b99bcf5da..c0c29a9ab 100644 --- a/crates/egui/src/containers/scroll_area.rs +++ b/crates/egui/src/containers/scroll_area.rs @@ -423,13 +423,6 @@ impl ScrollArea { self } - /// A source for the unique [`Id`], e.g. `.id_source("second_scroll_area")` or `.id_source(loop_index)`. - #[inline] - #[deprecated = "Renamed id_salt"] - pub fn id_source(self, id_salt: impl std::hash::Hash) -> Self { - self.id_salt(id_salt) - } - /// A source for the unique [`Id`], e.g. `.id_salt("second_scroll_area")` or `.id_salt(loop_index)`. #[inline] pub fn id_salt(mut self, id_salt: impl std::hash::Hash) -> Self { @@ -530,32 +523,6 @@ impl ScrollArea { /// This can be used, for example, to optionally freeze scrolling while the user /// is typing text in a [`crate::TextEdit`] widget contained within the scroll area. /// - /// This controls both scrolling directions. - #[deprecated = "Use `ScrollArea::scroll_source()"] - #[inline] - pub fn enable_scrolling(mut self, enable: bool) -> Self { - self.scroll_source = if enable { - ScrollSource::ALL - } else { - ScrollSource::NONE - }; - self - } - - /// Can the user drag the scroll area to scroll? - /// - /// This is useful for touch screens. - /// - /// If `true`, the [`ScrollArea`] will sense drags. - /// - /// Default: `true`. - #[deprecated = "Use `ScrollArea::scroll_source()"] - #[inline] - pub fn drag_to_scroll(mut self, drag_to_scroll: bool) -> Self { - self.scroll_source.drag = drag_to_scroll; - self - } - /// What sources does the [`ScrollArea`] use for scrolling the contents. #[inline] pub fn scroll_source(mut self, scroll_source: ScrollSource) -> Self { diff --git a/crates/egui/src/containers/tooltip.rs b/crates/egui/src/containers/tooltip.rs index 78c5a726b..22c319569 100644 --- a/crates/egui/src/containers/tooltip.rs +++ b/crates/egui/src/containers/tooltip.rs @@ -16,24 +16,6 @@ pub struct Tooltip<'a> { } impl Tooltip<'_> { - /// Show a tooltip that is always open. - #[deprecated = "Use `Tooltip::always_open` instead."] - pub fn new( - parent_widget: Id, - ctx: Context, - anchor: impl Into, - parent_layer: LayerId, - ) -> Self { - Self { - popup: Popup::new(parent_widget, ctx, anchor.into(), parent_layer) - .kind(PopupKind::Tooltip) - .gap(4.0) - .sense(Sense::hover()), - parent_layer, - parent_widget, - } - } - /// Show a tooltip that is always open. pub fn always_open( ctx: Context, diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 5ade37014..9f25d6131 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -262,7 +262,7 @@ impl<'open> Window<'open> { self } - /// Constrains this window to [`Context::screen_rect`]. + /// Constrains this window to [`Context::content_rect`]. /// /// To change the area to constrain to, use [`Self::constrain_to`]. /// @@ -275,7 +275,7 @@ impl<'open> Window<'open> { /// Constrain the movement of the window to the given rectangle. /// - /// For instance: `.constrain_to(ctx.screen_rect())`. + /// For instance: `.constrain_to(ctx.content_rect())`. #[inline] pub fn constrain_to(mut self, constrain_rect: Rect) -> Self { self.area = self.area.constrain_to(constrain_rect); @@ -427,7 +427,7 @@ impl<'open> Window<'open> { /// Enable/disable scrolling on the window by dragging with the pointer. `true` by default. /// - /// See [`ScrollArea::drag_to_scroll`] for more. + /// See [`ScrollArea::scroll_source`] for more. #[inline] pub fn drag_to_scroll(mut self, drag_to_scroll: bool) -> Self { self.scroll = self.scroll.scroll_source(ScrollSource { diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 433446648..d348517ef 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -300,7 +300,7 @@ impl RepaintCause { struct ViewportRepaintInfo { /// Monotonically increasing counter. /// - /// Incremented at the end of [`Context::run`]. + /// Incremented at the end of [`Context::run_ui`]. /// This can be smaller than [`Self::cumulative_pass_nr`], /// but never larger. cumulative_frame_nr: u64, @@ -463,7 +463,7 @@ impl ContextImpl { let content_rect = viewport.input.content_rect(); - viewport.this_pass.begin_pass(content_rect); + viewport.this_pass.begin_pass(); { let mut layers: Vec = viewport.prev_pass.widgets.layer_ids().collect(); @@ -697,8 +697,8 @@ impl ContextImpl { /// // Game loop: /// loop { /// let raw_input = egui::RawInput::default(); -/// let full_output = ctx.run(raw_input, |ctx| { -/// egui::CentralPanel::default().show(&ctx, |ui| { +/// let full_output = ctx.run_ui(raw_input, |ui| { +/// egui::CentralPanel::default().show_inside(ui, |ui| { /// ui.label("Hello world!"); /// if ui.button("Click me").clicked() { /// // take some action here @@ -780,9 +780,6 @@ impl Context { /// }); /// // handle full_output /// ``` - /// - /// ## See also - /// * [`Self::run`] #[must_use] pub fn run_ui(&self, new_input: RawInput, mut run_ui: impl FnMut(&mut Ui)) -> FullOutput { self.run_ui_dyn(new_input, &mut run_ui) @@ -791,14 +788,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| { + self.run_dyn(new_input, &mut |ctx| { let mut root_ui = Ui::new( ctx.clone(), Id::new((ctx.viewport_id(), "__top_ui")), UiBuilder::new() .layer_id(LayerId::background()) - .max_rect(ctx.available_rect()), + .max_rect(ctx.viewport_rect()), ); { @@ -814,38 +810,6 @@ impl Context { }) } - /// Run the ui code for one frame. - /// - /// At most [`Options::max_passes`] calls will be issued to `run_ui`, - /// and only on the rare occasion that [`Context::request_discard`] is called. - /// Usually, it `run_ui` will only be called once. - /// - /// Put your widgets into a [`crate::Panel`], [`crate::CentralPanel`], [`crate::Window`] or [`crate::Area`]. - /// - /// Instead of calling `run`, you can alternatively use [`Self::begin_pass`] and [`Context::end_pass`]. - /// - /// ``` - /// // One egui context that you keep reusing: - /// let mut ctx = egui::Context::default(); - /// - /// // Each frame: - /// let input = egui::RawInput::default(); - /// let full_output = ctx.run(input, |ctx| { - /// egui::CentralPanel::default().show(&ctx, |ui| { - /// ui.label("Hello egui!"); - /// }); - /// }); - /// // handle full_output - /// ``` - /// - /// ## 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) - } - #[must_use] fn run_dyn(&self, mut new_input: RawInput, run_ui: &mut dyn FnMut(&Self)) -> FullOutput { profiling::function_scope!(); @@ -915,10 +879,10 @@ impl Context { output } - /// An alternative to calling [`Self::run`]. + /// An alternative to calling [`Self::run_ui`]. /// - /// It is usually better to use [`Self::run`], because - /// `run` supports multi-pass layout using [`Self::request_discard`]. + /// It is usually better to use [`Self::run_ui`], because + /// `run_ui` supports multi-pass layout using [`Self::request_discard`]. /// /// ``` /// // One egui context that you keep reusing: @@ -928,9 +892,7 @@ impl Context { /// let input = egui::RawInput::default(); /// ctx.begin_pass(input); /// - /// egui::CentralPanel::default().show(&ctx, |ui| { - /// ui.label("Hello egui!"); - /// }); + /// // … add panels and windows here … /// /// let full_output = ctx.end_pass(); /// // handle full_output @@ -943,12 +905,6 @@ impl Context { self.write(|ctx| ctx.begin_pass(new_input)); } - - /// See [`Self::begin_pass`]. - #[deprecated = "Renamed begin_pass"] - pub fn begin_frame(&self, new_input: RawInput) { - self.begin_pass(new_input); - } } /// ## Borrows parts of [`Context`] @@ -1049,7 +1005,7 @@ impl Context { /// Read-only access to [`PassState`]. /// - /// This is only valid during the call to [`Self::run`] (between [`Self::begin_pass`] and [`Self::end_pass`]). + /// This is only valid during the call to [`Self::run_ui`] (between [`Self::begin_pass`] and [`Self::end_pass`]). #[inline] pub(crate) fn pass_state(&self, reader: impl FnOnce(&PassState) -> R) -> R { self.write(move |ctx| reader(&ctx.viewport().this_pass)) @@ -1057,7 +1013,7 @@ impl Context { /// Read-write access to [`PassState`]. /// - /// This is only valid during the call to [`Self::run`] (between [`Self::begin_pass`] and [`Self::end_pass`]). + /// This is only valid during the call to [`Self::run_ui`] (between [`Self::begin_pass`] and [`Self::end_pass`]). #[inline] pub(crate) fn pass_state_mut(&self, writer: impl FnOnce(&mut PassState) -> R) -> R { self.write(move |ctx| writer(&mut ctx.viewport().this_pass)) @@ -1073,7 +1029,7 @@ impl Context { /// Read-only access to [`Fonts`]. /// - /// Not valid until first call to [`Context::run()`]. + /// Not valid until first call to [`Context::run_ui()`]. /// That's because since we don't know the proper `pixels_per_point` until then. #[inline] pub fn fonts(&self, reader: impl FnOnce(&FontsView<'_>) -> R) -> R { @@ -1090,7 +1046,7 @@ impl Context { /// Read-write access to [`Fonts`]. /// - /// Not valid until first call to [`Context::run()`]. + /// Not valid until first call to [`Context::run_ui()`]. /// That's because since we don't know the proper `pixels_per_point` until then. #[inline] pub fn fonts_mut(&self, reader: impl FnOnce(&mut FontsView<'_>) -> R) -> R { @@ -1666,7 +1622,7 @@ impl Context { /// The total number of completed frames. /// - /// Starts at zero, and is incremented once at the end of each call to [`Self::run`]. + /// Starts at zero, and is incremented once at the end of each call to [`Self::run_ui`]. /// /// This is always smaller or equal to [`Self::cumulative_pass_nr`]. pub fn cumulative_frame_nr(&self) -> u64 { @@ -1675,7 +1631,7 @@ impl Context { /// The total number of completed frames. /// - /// Starts at zero, and is incremented once at the end of each call to [`Self::run`]. + /// Starts at zero, and is incremented once at the end of each call to [`Self::run_ui`]. /// /// This is always smaller or equal to [`Self::cumulative_pass_nr_for`]. pub fn cumulative_frame_nr_for(&self, id: ViewportId) -> u64 { @@ -1695,7 +1651,7 @@ impl Context { /// The total number of completed passes (usually there is one pass per rendered frame). /// - /// Starts at zero, and is incremented for each completed pass inside of [`Self::run`] (usually once). + /// Starts at zero, and is incremented for each completed pass inside of [`Self::run_ui`] (usually once). /// /// If you instead want to know which pass index this is within the current frame, /// use [`Self::current_pass_index`]. @@ -1705,7 +1661,7 @@ impl Context { /// The total number of completed passes (usually there is one pass per rendered frame). /// - /// Starts at zero, and is incremented for each completed pass inside of [`Self::run`] (usually once). + /// Starts at zero, and is incremented for each completed pass inside of [`Self::run_ui`] (usually once). pub fn cumulative_pass_nr_for(&self, id: ViewportId) -> u64 { self.read(|ctx| { ctx.viewports @@ -2080,7 +2036,7 @@ impl Context { self.options(|opt| opt.theme()) } - /// The [`Theme`] used to select between dark and light [`Self::style`] + /// The [`Theme`] used to select between dark and light [`Self::global_style`] /// as the active style used by all subsequent popups, menus, etc. /// /// Example: @@ -2097,12 +2053,6 @@ impl Context { self.options(|opt| Arc::clone(opt.style())) } - /// The currently active [`Style`] used by all subsequent popups, menus, etc. - #[deprecated = "Renamed to `global_style` to avoid confusion with `ui.style()`"] - pub fn style(&self) -> Arc