mirror of
https://github.com/emilk/egui.git
synced 2026-09-02 06:40:06 -04:00
Merge branch 'emilk:main' into main
This commit is contained in:
2
.github/workflows/cargo_machete.yml
vendored
2
.github/workflows/cargo_machete.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: 1.88
|
toolchain: 1.92
|
||||||
- name: Machete install
|
- name: Machete install
|
||||||
## The official cargo-machete action
|
## The official cargo-machete action
|
||||||
uses: bnjbvr/cargo-machete@v0.9.1
|
uses: bnjbvr/cargo-machete@v0.9.1
|
||||||
|
|||||||
2
.github/workflows/deploy_web_demo.yml
vendored
2
.github/workflows/deploy_web_demo.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
target: wasm32-unknown-unknown
|
target: wasm32-unknown-unknown
|
||||||
toolchain: 1.88.0
|
toolchain: 1.92.0
|
||||||
override: true
|
override: true
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
|||||||
2
.github/workflows/preview_build.yml
vendored
2
.github/workflows/preview_build.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: 1.88.0
|
toolchain: 1.92.0
|
||||||
targets: wasm32-unknown-unknown
|
targets: wasm32-unknown-unknown
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
14
.github/workflows/rust.yml
vendored
14
.github/workflows/rust.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: 1.88.0
|
toolchain: 1.92.0
|
||||||
|
|
||||||
- name: Install packages (Linux)
|
- name: Install packages (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
@@ -74,7 +74,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: 1.88.0
|
toolchain: 1.92.0
|
||||||
targets: wasm32-unknown-unknown
|
targets: wasm32-unknown-unknown
|
||||||
|
|
||||||
- run: sudo apt-get update && sudo apt-get install libgtk-3-dev libatk1.0-dev
|
- run: sudo apt-get update && sudo apt-get install libgtk-3-dev libatk1.0-dev
|
||||||
@@ -148,7 +148,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: EmbarkStudios/cargo-deny-action@v2
|
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||||
with:
|
with:
|
||||||
rust-version: "1.88.0"
|
rust-version: "1.92.0"
|
||||||
log-level: error
|
log-level: error
|
||||||
command: check
|
command: check
|
||||||
arguments: --target ${{ matrix.target }}
|
arguments: --target ${{ matrix.target }}
|
||||||
@@ -164,7 +164,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: 1.88.0
|
toolchain: 1.92.0
|
||||||
targets: aarch64-linux-android
|
targets: aarch64-linux-android
|
||||||
|
|
||||||
- name: Set up cargo cache
|
- name: Set up cargo cache
|
||||||
@@ -186,7 +186,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: 1.88.0
|
toolchain: 1.92.0
|
||||||
targets: aarch64-apple-ios
|
targets: aarch64-apple-ios
|
||||||
|
|
||||||
- name: Set up cargo cache
|
- name: Set up cargo cache
|
||||||
@@ -206,7 +206,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: 1.88.0
|
toolchain: 1.92.0
|
||||||
|
|
||||||
- name: Set up cargo cache
|
- name: Set up cargo cache
|
||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
@@ -228,7 +228,7 @@ jobs:
|
|||||||
lfs: true
|
lfs: true
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: 1.88.0
|
toolchain: 1.92.0
|
||||||
|
|
||||||
- name: Set up cargo cache
|
- name: Set up cargo cache
|
||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
[default.extend-words]
|
[default.extend-words]
|
||||||
ime = "ime" # Input Method Editor
|
ime = "ime" # Input Method Editor
|
||||||
nknown = "nknown" # part of @55nknown username
|
nknown = "nknown" # part of @55nknown username
|
||||||
isse = "isse" # part of @IsseW username
|
isse = "isse" # part of @IsseW username
|
||||||
tye = "tye" # part of @tye-exe username
|
tye = "tye" # part of @tye-exe username
|
||||||
ro = "ro" # read-only, also part of the username @Phen-Ro
|
ro = "ro" # read-only, also part of the username @Phen-Ro
|
||||||
typ = "typ" # Often used because `type` is a keyword in Rust
|
typ = "typ" # Often used because `type` is a keyword in Rust
|
||||||
|
|
||||||
|
|||||||
14
Cargo.toml
14
Cargo.toml
@@ -23,7 +23,7 @@ members = [
|
|||||||
[workspace.package]
|
[workspace.package]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
rust-version = "1.88"
|
rust-version = "1.92"
|
||||||
version = "0.33.3"
|
version = "0.33.3"
|
||||||
|
|
||||||
|
|
||||||
@@ -184,7 +184,9 @@ branches_sharing_code = "warn"
|
|||||||
char_lit_as_u8 = "warn"
|
char_lit_as_u8 = "warn"
|
||||||
checked_conversions = "warn"
|
checked_conversions = "warn"
|
||||||
clear_with_drain = "warn"
|
clear_with_drain = "warn"
|
||||||
|
clone_on_ref_ptr = "warn"
|
||||||
cloned_instead_of_copied = "warn"
|
cloned_instead_of_copied = "warn"
|
||||||
|
coerce_container_to_any = "warn"
|
||||||
dbg_macro = "warn"
|
dbg_macro = "warn"
|
||||||
debug_assert_with_mut_call = "warn"
|
debug_assert_with_mut_call = "warn"
|
||||||
default_union_representation = "warn"
|
default_union_representation = "warn"
|
||||||
@@ -194,7 +196,9 @@ disallowed_methods = "warn" # See clippy.toml
|
|||||||
disallowed_names = "warn" # See clippy.toml
|
disallowed_names = "warn" # See clippy.toml
|
||||||
disallowed_script_idents = "warn" # See clippy.toml
|
disallowed_script_idents = "warn" # See clippy.toml
|
||||||
disallowed_types = "warn" # See clippy.toml
|
disallowed_types = "warn" # See clippy.toml
|
||||||
|
doc_broken_link = "warn"
|
||||||
doc_comment_double_space_linebreaks = "warn"
|
doc_comment_double_space_linebreaks = "warn"
|
||||||
|
doc_include_without_cfg = "warn"
|
||||||
doc_link_with_quotes = "warn"
|
doc_link_with_quotes = "warn"
|
||||||
doc_markdown = "warn"
|
doc_markdown = "warn"
|
||||||
elidable_lifetime_names = "warn"
|
elidable_lifetime_names = "warn"
|
||||||
@@ -227,6 +231,7 @@ inefficient_to_string = "warn"
|
|||||||
infinite_loop = "warn"
|
infinite_loop = "warn"
|
||||||
into_iter_without_iter = "warn"
|
into_iter_without_iter = "warn"
|
||||||
invalid_upcast_comparisons = "warn"
|
invalid_upcast_comparisons = "warn"
|
||||||
|
ip_constant = "warn"
|
||||||
iter_filter_is_ok = "warn"
|
iter_filter_is_ok = "warn"
|
||||||
iter_filter_is_some = "warn"
|
iter_filter_is_some = "warn"
|
||||||
iter_not_returning_iterator = "warn"
|
iter_not_returning_iterator = "warn"
|
||||||
@@ -299,6 +304,7 @@ ref_patterns = "warn"
|
|||||||
rest_pat_in_fully_bound_structs = "warn"
|
rest_pat_in_fully_bound_structs = "warn"
|
||||||
return_and_then = "warn"
|
return_and_then = "warn"
|
||||||
same_functions_in_if_condition = "warn"
|
same_functions_in_if_condition = "warn"
|
||||||
|
self_only_used_in_recursion = "warn"
|
||||||
semicolon_if_nothing_returned = "warn"
|
semicolon_if_nothing_returned = "warn"
|
||||||
set_contains_or_insert = "warn"
|
set_contains_or_insert = "warn"
|
||||||
single_char_pattern = "warn"
|
single_char_pattern = "warn"
|
||||||
@@ -310,7 +316,6 @@ string_add = "warn"
|
|||||||
string_add_assign = "warn"
|
string_add_assign = "warn"
|
||||||
string_lit_as_bytes = "warn"
|
string_lit_as_bytes = "warn"
|
||||||
string_lit_chars_any = "warn"
|
string_lit_chars_any = "warn"
|
||||||
string_to_string = "warn"
|
|
||||||
suspicious_command_arg_space = "warn"
|
suspicious_command_arg_space = "warn"
|
||||||
suspicious_xor_used_as_pow = "warn"
|
suspicious_xor_used_as_pow = "warn"
|
||||||
todo = "warn"
|
todo = "warn"
|
||||||
@@ -319,7 +324,7 @@ trailing_empty_array = "warn"
|
|||||||
trait_duplication_in_bounds = "warn"
|
trait_duplication_in_bounds = "warn"
|
||||||
transmute_ptr_to_ptr = "warn"
|
transmute_ptr_to_ptr = "warn"
|
||||||
tuple_array_conversions = "warn"
|
tuple_array_conversions = "warn"
|
||||||
unchecked_duration_subtraction = "warn"
|
unchecked_time_subtraction = "warn"
|
||||||
undocumented_unsafe_blocks = "warn"
|
undocumented_unsafe_blocks = "warn"
|
||||||
unimplemented = "warn"
|
unimplemented = "warn"
|
||||||
uninhabited_references = "warn"
|
uninhabited_references = "warn"
|
||||||
@@ -339,6 +344,7 @@ unused_peekable = "warn"
|
|||||||
unused_rounding = "warn"
|
unused_rounding = "warn"
|
||||||
unused_self = "warn"
|
unused_self = "warn"
|
||||||
unused_trait_names = "warn"
|
unused_trait_names = "warn"
|
||||||
|
unwrap_used = "warn"
|
||||||
use_self = "warn"
|
use_self = "warn"
|
||||||
useless_let_if_seq = "warn"
|
useless_let_if_seq = "warn"
|
||||||
useless_transmute = "warn"
|
useless_transmute = "warn"
|
||||||
@@ -348,10 +354,10 @@ zero_sized_map_values = "warn"
|
|||||||
|
|
||||||
|
|
||||||
# TODO(emilk): maybe enable more of these lints?
|
# TODO(emilk): maybe enable more of these lints?
|
||||||
|
cast_possible_wrap = "allow"
|
||||||
comparison_chain = "allow"
|
comparison_chain = "allow"
|
||||||
should_panic_without_expect = "allow"
|
should_panic_without_expect = "allow"
|
||||||
too_many_lines = "allow"
|
too_many_lines = "allow"
|
||||||
unwrap_used = "allow" # TODO(emilk): We really wanna warn on this one
|
|
||||||
|
|
||||||
# These are meh:
|
# These are meh:
|
||||||
assigning_clones = "allow" # No please
|
assigning_clones = "allow" # No please
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Section identical to scripts/clippy_wasm/clippy.toml:
|
# Section identical to scripts/clippy_wasm/clippy.toml:
|
||||||
|
|
||||||
msrv = "1.88"
|
msrv = "1.92"
|
||||||
|
|
||||||
allow-unwrap-in-tests = true
|
allow-unwrap-in-tests = true
|
||||||
|
|
||||||
@@ -39,9 +39,9 @@ disallowed-methods = [
|
|||||||
# but we cannot disable them all here (because of e.g. https://github.com/rust-lang/rust-clippy/issues/10406)
|
# but we cannot disable them all here (because of e.g. https://github.com/rust-lang/rust-clippy/issues/10406)
|
||||||
# so we do that in `clipppy_wasm.toml` instead.
|
# so we do that in `clipppy_wasm.toml` instead.
|
||||||
|
|
||||||
{ path = "std::env::temp_dir", readon = "Use the tempfile crate instead" },
|
{ path = "std::env::temp_dir", reason = "Use the tempfile crate instead" },
|
||||||
{ path = "std::panic::catch_unwind", reason = "We compile with `panic = abort" },
|
{ path = "std::panic::catch_unwind", reason = "We compile with `panic = abort" },
|
||||||
{ path = "std::thread::spawn", readon = "Use `std::thread::Builder` and name the thread" },
|
{ path = "std::thread::spawn", reason = "Use `std::thread::Builder` and name the thread" },
|
||||||
]
|
]
|
||||||
|
|
||||||
# https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names
|
# https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ impl Hsva {
|
|||||||
/// From linear RGBA with premultiplied alpha
|
/// From linear RGBA with premultiplied alpha
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_rgba_premultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {
|
pub fn from_rgba_premultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||||
#![allow(clippy::many_single_char_names)]
|
#![expect(clippy::many_single_char_names)]
|
||||||
if a <= 0.0 {
|
if a <= 0.0 {
|
||||||
if r == 0.0 && b == 0.0 && a == 0.0 {
|
if r == 0.0 && b == 0.0 && a == 0.0 {
|
||||||
Self::default()
|
Self::default()
|
||||||
@@ -57,7 +57,7 @@ impl Hsva {
|
|||||||
/// From linear RGBA without premultiplied alpha
|
/// From linear RGBA without premultiplied alpha
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_rgba_unmultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {
|
pub fn from_rgba_unmultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||||
#![allow(clippy::many_single_char_names)]
|
#![expect(clippy::many_single_char_names)]
|
||||||
let (h, s, v) = hsv_from_rgb([r, g, b]);
|
let (h, s, v) = hsv_from_rgb([r, g, b]);
|
||||||
Self { h, s, v, a }
|
Self { h, s, v, a }
|
||||||
}
|
}
|
||||||
@@ -189,7 +189,7 @@ impl From<Color32> for Hsva {
|
|||||||
/// All ranges in 0-1, rgb is linear.
|
/// All ranges in 0-1, rgb is linear.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hsv_from_rgb([r, g, b]: [f32; 3]) -> (f32, f32, f32) {
|
pub fn hsv_from_rgb([r, g, b]: [f32; 3]) -> (f32, f32, f32) {
|
||||||
#![allow(clippy::many_single_char_names)]
|
#![expect(clippy::many_single_char_names)]
|
||||||
let min = r.min(g.min(b));
|
let min = r.min(g.min(b));
|
||||||
let max = r.max(g.max(b)); // value
|
let max = r.max(g.max(b)); // value
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ pub fn hsv_from_rgb([r, g, b]: [f32; 3]) -> (f32, f32, f32) {
|
|||||||
/// All ranges in 0-1, rgb is linear.
|
/// All ranges in 0-1, rgb is linear.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn rgb_from_hsv((h, s, v): (f32, f32, f32)) -> [f32; 3] {
|
pub fn rgb_from_hsv((h, s, v): (f32, f32, f32)) -> [f32; 3] {
|
||||||
#![allow(clippy::many_single_char_names)]
|
#![expect(clippy::many_single_char_names)]
|
||||||
let h = (h.fract() + 1.0).fract(); // wrap
|
let h = (h.fract() + 1.0).fract(); // wrap
|
||||||
let s = s.clamp(0.0, 1.0);
|
let s = s.clamp(0.0, 1.0);
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
|
#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
|
||||||
//!
|
//!
|
||||||
|
|
||||||
#![allow(clippy::wrong_self_convention)]
|
#![expect(clippy::wrong_self_convention)]
|
||||||
|
|
||||||
#[cfg(feature = "cint")]
|
#[cfg(feature = "cint")]
|
||||||
mod cint_impl;
|
mod cint_impl;
|
||||||
|
|||||||
@@ -251,7 +251,10 @@ web-sys = { workspace = true, features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
# optional web:
|
# optional web:
|
||||||
egui-wgpu = { workspace = true, optional = true, features = ["capture"] } # if wgpu is used, use it without (!) winit
|
egui-wgpu = { workspace = true, optional = true, features = [
|
||||||
|
# if wgpu is used, use it without (!) winit:
|
||||||
|
"capture",
|
||||||
|
] }
|
||||||
wgpu = { workspace = true, optional = true }
|
wgpu = { workspace = true, optional = true }
|
||||||
|
|
||||||
# Native dev dependencies for testing
|
# Native dev dependencies for testing
|
||||||
|
|||||||
@@ -791,6 +791,7 @@ impl Frame {
|
|||||||
/// This function will take the ownership of your [`glow::Texture`], so please do not delete your [`glow::Texture`] after registering.
|
/// This function will take the ownership of your [`glow::Texture`], so please do not delete your [`glow::Texture`] after registering.
|
||||||
#[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
|
||||||
pub fn register_native_glow_texture(&mut self, native: glow::Texture) -> egui::TextureId {
|
pub fn register_native_glow_texture(&mut self, native: glow::Texture) -> egui::TextureId {
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
self.glow_register_native_texture.as_mut().unwrap()(native)
|
self.glow_register_native_texture.as_mut().unwrap()(native)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -142,7 +142,6 @@
|
|||||||
//!
|
//!
|
||||||
|
|
||||||
#![warn(missing_docs)] // let's keep eframe well-documented
|
#![warn(missing_docs)] // let's keep eframe well-documented
|
||||||
#![allow(clippy::needless_doctest_main)]
|
|
||||||
|
|
||||||
// Limitation imposed by `accesskit_winit`:
|
// Limitation imposed by `accesskit_winit`:
|
||||||
// https://github.com/AccessKit/accesskit/tree/accesskit-v0.18.0/platforms/winit#android-activity-compatibility`
|
// https://github.com/AccessKit/accesskit/tree/accesskit-v0.18.0/platforms/winit#android-activity-compatibility`
|
||||||
@@ -253,7 +252,7 @@ pub mod icon_data;
|
|||||||
/// This function can fail if we fail to set up a graphics context.
|
/// This function can fail if we fail to set up a graphics context.
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
#[allow(clippy::needless_pass_by_value, clippy::allow_attributes)]
|
#[allow(clippy::allow_attributes, clippy::needless_pass_by_value)]
|
||||||
pub fn run_native(
|
pub fn run_native(
|
||||||
app_name: &str,
|
app_name: &str,
|
||||||
mut native_options: NativeOptions,
|
mut native_options: NativeOptions,
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ enum AppIconStatus {
|
|||||||
NotSetTryAgain,
|
NotSetTryAgain,
|
||||||
|
|
||||||
/// We successfully set the icon and it should be visible now.
|
/// We successfully set the icon and it should be visible now.
|
||||||
#[allow(dead_code, clippy::allow_attributes)] // Not used on Linux
|
#[allow(clippy::allow_attributes, dead_code)] // Not used on Linux
|
||||||
Set,
|
Set,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ fn set_title_and_icon(_title: &str, _icon_data: Option<&IconData>) -> AppIconSta
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
return set_title_and_icon_mac(_title, _icon_data);
|
return set_title_and_icon_mac(_title, _icon_data);
|
||||||
|
|
||||||
#[allow(unreachable_code, clippy::allow_attributes)]
|
#[allow(clippy::allow_attributes, unreachable_code)]
|
||||||
AppIconStatus::NotSetIgnored
|
AppIconStatus::NotSetIgnored
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -376,6 +376,7 @@ impl EpiIntegration {
|
|||||||
|
|
||||||
fn load_default_egui_icon() -> egui::IconData {
|
fn load_default_egui_icon() -> egui::IconData {
|
||||||
profiling::function_scope!();
|
profiling::function_scope!();
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
crate::icon_data::from_png_bytes(&include_bytes!("../../data/icon.png")[..]).unwrap()
|
crate::icon_data::from_png_bytes(&include_bytes!("../../data/icon.png")[..]).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
//! There is a bunch of improvements we could do,
|
//! There is a bunch of improvements we could do,
|
||||||
//! like removing a bunch of `unwraps`.
|
//! like removing a bunch of `unwraps`.
|
||||||
|
|
||||||
#![allow(clippy::undocumented_unsafe_blocks)]
|
#![expect(clippy::undocumented_unsafe_blocks)]
|
||||||
|
#![expect(clippy::unwrap_used)]
|
||||||
|
|
||||||
use std::{cell::RefCell, num::NonZeroU32, rc::Rc, sync::Arc, time::Instant};
|
use std::{cell::RefCell, num::NonZeroU32, rc::Rc, sync::Arc, time::Instant};
|
||||||
|
|
||||||
@@ -216,7 +217,7 @@ impl<'app> GlowWinitApp<'app> {
|
|||||||
storage.as_deref(),
|
storage.as_deref(),
|
||||||
&mut self.native_options,
|
&mut self.native_options,
|
||||||
)?;
|
)?;
|
||||||
let gl = painter.gl().clone();
|
let gl = Arc::clone(painter.gl());
|
||||||
|
|
||||||
let max_texture_side = painter.max_texture_side();
|
let max_texture_side = painter.max_texture_side();
|
||||||
glutin.max_texture_side = Some(max_texture_side);
|
glutin.max_texture_side = Some(max_texture_side);
|
||||||
@@ -234,9 +235,9 @@ impl<'app> GlowWinitApp<'app> {
|
|||||||
&self.app_name,
|
&self.app_name,
|
||||||
&self.native_options,
|
&self.native_options,
|
||||||
storage,
|
storage,
|
||||||
Some(gl.clone()),
|
Some(Arc::clone(&gl)),
|
||||||
Some(Box::new({
|
Some(Box::new({
|
||||||
let painter = painter.clone();
|
let painter = Rc::clone(&painter);
|
||||||
move |native| painter.borrow_mut().register_native_texture(native)
|
move |native| painter.borrow_mut().register_native_texture(native)
|
||||||
})),
|
})),
|
||||||
#[cfg(feature = "wgpu_no_default_features")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
@@ -244,7 +245,7 @@ impl<'app> GlowWinitApp<'app> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
{
|
{
|
||||||
let event_loop_proxy = self.repaint_proxy.clone();
|
let event_loop_proxy = Arc::clone(&self.repaint_proxy);
|
||||||
integration
|
integration
|
||||||
.egui_ctx
|
.egui_ctx
|
||||||
.set_request_repaint_callback(move |info| {
|
.set_request_repaint_callback(move |info| {
|
||||||
|
|||||||
@@ -349,8 +349,6 @@ pub fn run_glow(
|
|||||||
mut native_options: epi::NativeOptions,
|
mut native_options: epi::NativeOptions,
|
||||||
app_creator: epi::AppCreator<'_>,
|
app_creator: epi::AppCreator<'_>,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
#![allow(clippy::needless_return_with_question_mark)] // False positive
|
|
||||||
|
|
||||||
use super::glow_integration::GlowWinitApp;
|
use super::glow_integration::GlowWinitApp;
|
||||||
|
|
||||||
#[cfg(not(target_os = "ios"))]
|
#[cfg(not(target_os = "ios"))]
|
||||||
@@ -387,8 +385,6 @@ pub fn run_wgpu(
|
|||||||
mut native_options: epi::NativeOptions,
|
mut native_options: epi::NativeOptions,
|
||||||
app_creator: epi::AppCreator<'_>,
|
app_creator: epi::AppCreator<'_>,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
#![allow(clippy::needless_return_with_question_mark)] // False positive
|
|
||||||
|
|
||||||
use super::wgpu_integration::WgpuWinitApp;
|
use super::wgpu_integration::WgpuWinitApp;
|
||||||
|
|
||||||
#[cfg(not(target_os = "ios"))]
|
#[cfg(not(target_os = "ios"))]
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ impl<'app> WgpuWinitApp<'app> {
|
|||||||
|
|
||||||
{
|
{
|
||||||
profiling::scope!("set_window");
|
profiling::scope!("set_window");
|
||||||
pollster::block_on(painter.set_window(ViewportId::ROOT, Some(window.clone())))?;
|
pollster::block_on(painter.set_window(ViewportId::ROOT, Some(Arc::clone(&window))))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let wgpu_render_state = painter.render_state();
|
let wgpu_render_state = painter.render_state();
|
||||||
@@ -238,7 +238,7 @@ impl<'app> WgpuWinitApp<'app> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
{
|
{
|
||||||
let event_loop_proxy = self.repaint_proxy.clone();
|
let event_loop_proxy = Arc::clone(&self.repaint_proxy);
|
||||||
|
|
||||||
egui_ctx.set_request_repaint_callback(move |info| {
|
egui_ctx.set_request_repaint_callback(move |info| {
|
||||||
log::trace!("request_repaint_callback: {info:?}");
|
log::trace!("request_repaint_callback: {info:?}");
|
||||||
@@ -256,7 +256,7 @@ impl<'app> WgpuWinitApp<'app> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_mut, clippy::allow_attributes)] // used for accesskit
|
#[allow(clippy::allow_attributes, unused_mut)] // used for accesskit
|
||||||
let mut egui_winit = egui_winit::State::new(
|
let mut egui_winit = egui_winit::State::new(
|
||||||
egui_ctx.clone(),
|
egui_ctx.clone(),
|
||||||
ViewportId::ROOT,
|
ViewportId::ROOT,
|
||||||
@@ -610,7 +610,7 @@ impl WgpuWinitRunning<'_> {
|
|||||||
|
|
||||||
{
|
{
|
||||||
profiling::scope!("set_window");
|
profiling::scope!("set_window");
|
||||||
pollster::block_on(painter.set_window(viewport_id, Some(window.clone())))?;
|
pollster::block_on(painter.set_window(viewport_id, Some(Arc::clone(window))))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(egui_winit) = egui_winit.as_mut() else {
|
let Some(egui_winit) = egui_winit.as_mut() else {
|
||||||
@@ -919,7 +919,7 @@ impl Viewport {
|
|||||||
let window = Arc::new(window);
|
let window = Arc::new(window);
|
||||||
|
|
||||||
if let Err(err) =
|
if let Err(err) =
|
||||||
pollster::block_on(painter.set_window(viewport_id, Some(window.clone())))
|
pollster::block_on(painter.set_window(viewport_id, Some(Arc::clone(&window))))
|
||||||
{
|
{
|
||||||
log::error!("on set_window: viewport_id {viewport_id:?} {err}");
|
log::error!("on set_window: viewport_id {viewport_id:?} {err}");
|
||||||
}
|
}
|
||||||
@@ -1051,7 +1051,8 @@ fn render_immediate_viewport(
|
|||||||
|
|
||||||
{
|
{
|
||||||
profiling::scope!("set_window");
|
profiling::scope!("set_window");
|
||||||
if let Err(err) = pollster::block_on(painter.set_window(ids.this, Some(window.clone()))) {
|
if let Err(err) = pollster::block_on(painter.set_window(ids.this, Some(Arc::clone(window))))
|
||||||
|
{
|
||||||
log::error!(
|
log::error!(
|
||||||
"when rendering viewport_id={:?}, set_window Error {err}",
|
"when rendering viewport_id={:?}, set_window Error {err}",
|
||||||
ids.this
|
ids.this
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ pub fn create_egui_context(storage: Option<&dyn crate::Storage>) -> egui::Contex
|
|||||||
|
|
||||||
egui_ctx.options_mut(|o| {
|
egui_ctx.options_mut(|o| {
|
||||||
// eframe supports multi-pass (Context::request_discard).
|
// eframe supports multi-pass (Context::request_discard).
|
||||||
o.max_passes = 2.try_into().unwrap();
|
#[expect(clippy::unwrap_used)]
|
||||||
|
{
|
||||||
|
o.max_passes = 2.try_into().unwrap();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let memory = crate::native::epi_integration::load_egui_memory(storage).unwrap_or_default();
|
let memory = crate::native::epi_integration::load_egui_memory(storage).unwrap_or_default();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![allow(dead_code)] // not everything is used on wasm
|
#![allow(clippy::allow_attributes, dead_code)] // not used on all platforms
|
||||||
|
|
||||||
use web_time::Instant;
|
use web_time::Instant;
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ impl Stopwatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn pause(&mut self) {
|
pub fn pause(&mut self) {
|
||||||
let start = self.start.take().unwrap();
|
let start = self.start.take().expect("Stopwatch is not running");
|
||||||
let duration = start.elapsed();
|
let duration = start.elapsed();
|
||||||
self.total_time_ns += duration.as_nanos();
|
self.total_time_ns += duration.as_nanos();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use egui::{TexturesDelta, UserData, ViewportCommand};
|
use egui::{TexturesDelta, UserData, ViewportCommand};
|
||||||
|
|
||||||
use crate::{App, epi, web::web_painter::WebPainter};
|
use crate::{App, epi, web::web_painter::WebPainter};
|
||||||
@@ -5,14 +7,14 @@ use crate::{App, epi, web::web_painter::WebPainter};
|
|||||||
use super::{NeedRepaint, now_sec, text_agent::TextAgent};
|
use super::{NeedRepaint, now_sec, text_agent::TextAgent};
|
||||||
|
|
||||||
pub struct AppRunner {
|
pub struct AppRunner {
|
||||||
#[allow(dead_code, clippy::allow_attributes)]
|
#[allow(clippy::allow_attributes, dead_code)]
|
||||||
pub(crate) web_options: crate::WebOptions,
|
pub(crate) web_options: crate::WebOptions,
|
||||||
pub(crate) frame: epi::Frame,
|
pub(crate) frame: epi::Frame,
|
||||||
egui_ctx: egui::Context,
|
egui_ctx: egui::Context,
|
||||||
painter: Box<dyn WebPainter>,
|
painter: Box<dyn WebPainter>,
|
||||||
pub(crate) input: super::WebInput,
|
pub(crate) input: super::WebInput,
|
||||||
app: Box<dyn epi::App>,
|
app: Box<dyn epi::App>,
|
||||||
pub(crate) needs_repaint: std::sync::Arc<NeedRepaint>,
|
pub(crate) needs_repaint: Arc<NeedRepaint>,
|
||||||
last_save_time: f64,
|
last_save_time: f64,
|
||||||
pub(crate) text_agent: TextAgent,
|
pub(crate) text_agent: TextAgent,
|
||||||
|
|
||||||
@@ -63,7 +65,7 @@ impl AppRunner {
|
|||||||
canvas,
|
canvas,
|
||||||
&web_options,
|
&web_options,
|
||||||
)?;
|
)?;
|
||||||
gl = Some(painter.gl().clone());
|
gl = Some(Arc::clone(painter.gl()));
|
||||||
Box::new(painter) as Box<dyn WebPainter>
|
Box::new(painter) as Box<dyn WebPainter>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,10 +140,9 @@ impl AppRunner {
|
|||||||
wgpu_render_state,
|
wgpu_render_state,
|
||||||
};
|
};
|
||||||
|
|
||||||
let needs_repaint: std::sync::Arc<NeedRepaint> =
|
let needs_repaint: Arc<NeedRepaint> = Arc::new(NeedRepaint::new(web_options.max_fps));
|
||||||
std::sync::Arc::new(NeedRepaint::new(web_options.max_fps));
|
|
||||||
{
|
{
|
||||||
let needs_repaint = needs_repaint.clone();
|
let needs_repaint = Arc::clone(&needs_repaint);
|
||||||
egui_ctx.set_request_repaint_callback(move |info| {
|
egui_ctx.set_request_repaint_callback(move |info| {
|
||||||
needs_repaint.repaint_after(info.delay.as_secs_f64());
|
needs_repaint.repaint_after(info.delay.as_secs_f64());
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -137,25 +137,24 @@ fn install_keydown(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), J
|
|||||||
&& !modifiers.command
|
&& !modifiers.command
|
||||||
// When text agent is focused, it is responsible for handling input events
|
// When text agent is focused, it is responsible for handling input events
|
||||||
&& !runner.text_agent.has_focus()
|
&& !runner.text_agent.has_focus()
|
||||||
|
&& let Some(text) = text_from_keyboard_event(&event)
|
||||||
{
|
{
|
||||||
if let Some(text) = text_from_keyboard_event(&event) {
|
let egui_event = egui::Event::Text(text);
|
||||||
let egui_event = egui::Event::Text(text);
|
let should_stop_propagation =
|
||||||
let should_stop_propagation =
|
(runner.web_options.should_stop_propagation)(&egui_event);
|
||||||
(runner.web_options.should_stop_propagation)(&egui_event);
|
let should_prevent_default =
|
||||||
let should_prevent_default =
|
(runner.web_options.should_prevent_default)(&egui_event);
|
||||||
(runner.web_options.should_prevent_default)(&egui_event);
|
runner.input.raw.events.push(egui_event);
|
||||||
runner.input.raw.events.push(egui_event);
|
runner.needs_repaint.repaint_asap();
|
||||||
runner.needs_repaint.repaint_asap();
|
|
||||||
|
|
||||||
// If this is indeed text, then prevent any other action.
|
// If this is indeed text, then prevent any other action.
|
||||||
if should_prevent_default {
|
if should_prevent_default {
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use web options to tell if the event should be propagated to parent elements.
|
// Use web options to tell if the event should be propagated to parent elements.
|
||||||
if should_stop_propagation {
|
if should_stop_propagation {
|
||||||
event.stop_propagation();
|
event.stop_propagation();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,30 +320,28 @@ fn install_copy_cut_paste(runner_ref: &WebRunner, target: &EventTarget) -> Resul
|
|||||||
return; // The eframe app is not interested
|
return; // The eframe app is not interested
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(data) = event.clipboard_data() {
|
if let Some(data) = event.clipboard_data()
|
||||||
if let Ok(text) = data.get_data("text") {
|
&& let Ok(text) = data.get_data("text")
|
||||||
let text = text.replace("\r\n", "\n");
|
{
|
||||||
|
let text = text.replace("\r\n", "\n");
|
||||||
|
|
||||||
let mut should_stop_propagation = true;
|
let mut should_stop_propagation = true;
|
||||||
let mut should_prevent_default = true;
|
let mut should_prevent_default = true;
|
||||||
if !text.is_empty() {
|
if !text.is_empty() {
|
||||||
let egui_event = egui::Event::Paste(text);
|
let egui_event = egui::Event::Paste(text);
|
||||||
should_stop_propagation =
|
should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);
|
||||||
(runner.web_options.should_stop_propagation)(&egui_event);
|
should_prevent_default = (runner.web_options.should_prevent_default)(&egui_event);
|
||||||
should_prevent_default =
|
runner.input.raw.events.push(egui_event);
|
||||||
(runner.web_options.should_prevent_default)(&egui_event);
|
runner.needs_repaint.repaint_asap();
|
||||||
runner.input.raw.events.push(egui_event);
|
}
|
||||||
runner.needs_repaint.repaint_asap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
|
// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
|
||||||
if should_stop_propagation {
|
if should_stop_propagation {
|
||||||
event.stop_propagation();
|
event.stop_propagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
if should_prevent_default {
|
if should_prevent_default {
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
@@ -562,45 +559,44 @@ fn install_pointerup(runner_ref: &WebRunner, target: &EventTarget) -> Result<(),
|
|||||||
if is_interested_in_pointer_event(
|
if is_interested_in_pointer_event(
|
||||||
runner,
|
runner,
|
||||||
egui::pos2(event.client_x() as f32, event.client_y() as f32),
|
egui::pos2(event.client_x() as f32, event.client_y() as f32),
|
||||||
) {
|
) && let Some(button) = button_from_mouse_event(&event)
|
||||||
if let Some(button) = button_from_mouse_event(&event) {
|
{
|
||||||
let modifiers = runner.input.raw.modifiers;
|
let modifiers = runner.input.raw.modifiers;
|
||||||
let egui_event = egui::Event::PointerButton {
|
let egui_event = egui::Event::PointerButton {
|
||||||
pos,
|
pos,
|
||||||
button,
|
button,
|
||||||
pressed: false,
|
pressed: false,
|
||||||
modifiers,
|
modifiers,
|
||||||
};
|
};
|
||||||
let should_stop_propagation =
|
let should_stop_propagation =
|
||||||
(runner.web_options.should_stop_propagation)(&egui_event);
|
(runner.web_options.should_stop_propagation)(&egui_event);
|
||||||
let should_prevent_default =
|
let should_prevent_default =
|
||||||
(runner.web_options.should_prevent_default)(&egui_event);
|
(runner.web_options.should_prevent_default)(&egui_event);
|
||||||
runner.input.raw.events.push(egui_event);
|
runner.input.raw.events.push(egui_event);
|
||||||
|
|
||||||
// Previously on iOS, the canvas would not receive focus on
|
// Previously on iOS, the canvas would not receive focus on
|
||||||
// any touch event, which resulted in the on-screen keyboard
|
// any touch event, which resulted in the on-screen keyboard
|
||||||
// not working when focusing on a text field in an egui app.
|
// not working when focusing on a text field in an egui app.
|
||||||
// This attempts to fix that by forcing the focus on any
|
// This attempts to fix that by forcing the focus on any
|
||||||
// click on the canvas.
|
// click on the canvas.
|
||||||
runner.canvas().focus().ok();
|
runner.canvas().focus().ok();
|
||||||
|
|
||||||
// In Safari we are only allowed to do certain things
|
// In Safari we are only allowed to do certain things
|
||||||
// (like playing audio, start a download, etc)
|
// (like playing audio, start a download, etc)
|
||||||
// on user action, such as a click.
|
// on user action, such as a click.
|
||||||
// So we need to run the app logic here and now:
|
// So we need to run the app logic here and now:
|
||||||
runner.logic();
|
runner.logic();
|
||||||
|
|
||||||
// Make sure we paint the output of the above logic call asap:
|
// Make sure we paint the output of the above logic call asap:
|
||||||
runner.needs_repaint.repaint_asap();
|
runner.needs_repaint.repaint_asap();
|
||||||
|
|
||||||
if should_prevent_default {
|
if should_prevent_default {
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
|
// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
|
||||||
if should_stop_propagation {
|
if should_stop_propagation {
|
||||||
event.stop_propagation();
|
event.stop_propagation();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -713,29 +709,27 @@ fn install_touchstart(runner_ref: &WebRunner, target: &EventTarget) -> Result<()
|
|||||||
|
|
||||||
fn install_touchmove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {
|
fn install_touchmove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {
|
||||||
runner_ref.add_event_listener(target, "touchmove", |event: web_sys::TouchEvent, runner| {
|
runner_ref.add_event_listener(target, "touchmove", |event: web_sys::TouchEvent, runner| {
|
||||||
if let Some((pos, touch)) = primary_touch_pos(runner, &event) {
|
if let Some((pos, touch)) = primary_touch_pos(runner, &event)
|
||||||
if is_interested_in_pointer_event(
|
&& is_interested_in_pointer_event(
|
||||||
runner,
|
runner,
|
||||||
egui::pos2(touch.client_x() as f32, touch.client_y() as f32),
|
egui::pos2(touch.client_x() as f32, touch.client_y() as f32),
|
||||||
) {
|
)
|
||||||
let egui_event = egui::Event::PointerMoved(pos);
|
{
|
||||||
let should_stop_propagation =
|
let egui_event = egui::Event::PointerMoved(pos);
|
||||||
(runner.web_options.should_stop_propagation)(&egui_event);
|
let should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);
|
||||||
let should_prevent_default =
|
let should_prevent_default = (runner.web_options.should_prevent_default)(&egui_event);
|
||||||
(runner.web_options.should_prevent_default)(&egui_event);
|
runner.input.raw.events.push(egui_event);
|
||||||
runner.input.raw.events.push(egui_event);
|
|
||||||
|
|
||||||
push_touches(runner, egui::TouchPhase::Move, &event);
|
push_touches(runner, egui::TouchPhase::Move, &event);
|
||||||
runner.needs_repaint.repaint();
|
runner.needs_repaint.repaint();
|
||||||
|
|
||||||
// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
|
// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
|
||||||
if should_stop_propagation {
|
if should_stop_propagation {
|
||||||
event.stop_propagation();
|
event.stop_propagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
if should_prevent_default {
|
if should_prevent_default {
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -743,50 +737,49 @@ fn install_touchmove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(),
|
|||||||
|
|
||||||
fn install_touchend(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {
|
fn install_touchend(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {
|
||||||
runner_ref.add_event_listener(target, "touchend", |event: web_sys::TouchEvent, runner| {
|
runner_ref.add_event_listener(target, "touchend", |event: web_sys::TouchEvent, runner| {
|
||||||
if let Some((pos, touch)) = primary_touch_pos(runner, &event) {
|
if let Some((pos, touch)) = primary_touch_pos(runner, &event)
|
||||||
if is_interested_in_pointer_event(
|
&& is_interested_in_pointer_event(
|
||||||
runner,
|
runner,
|
||||||
egui::pos2(touch.client_x() as f32, touch.client_y() as f32),
|
egui::pos2(touch.client_x() as f32, touch.client_y() as f32),
|
||||||
) {
|
)
|
||||||
// First release mouse to click:
|
{
|
||||||
let mut should_stop_propagation = true;
|
// First release mouse to click:
|
||||||
let mut should_prevent_default = true;
|
let mut should_stop_propagation = true;
|
||||||
let egui_event = egui::Event::PointerButton {
|
let mut should_prevent_default = true;
|
||||||
pos,
|
let egui_event = egui::Event::PointerButton {
|
||||||
button: egui::PointerButton::Primary,
|
pos,
|
||||||
pressed: false,
|
button: egui::PointerButton::Primary,
|
||||||
modifiers: runner.input.raw.modifiers,
|
pressed: false,
|
||||||
};
|
modifiers: runner.input.raw.modifiers,
|
||||||
should_stop_propagation &=
|
};
|
||||||
(runner.web_options.should_stop_propagation)(&egui_event);
|
should_stop_propagation &= (runner.web_options.should_stop_propagation)(&egui_event);
|
||||||
should_prevent_default &= (runner.web_options.should_prevent_default)(&egui_event);
|
should_prevent_default &= (runner.web_options.should_prevent_default)(&egui_event);
|
||||||
runner.input.raw.events.push(egui_event);
|
runner.input.raw.events.push(egui_event);
|
||||||
// Then remove hover effect:
|
// Then remove hover effect:
|
||||||
should_stop_propagation &=
|
should_stop_propagation &=
|
||||||
(runner.web_options.should_stop_propagation)(&egui::Event::PointerGone);
|
(runner.web_options.should_stop_propagation)(&egui::Event::PointerGone);
|
||||||
should_prevent_default &=
|
should_prevent_default &=
|
||||||
(runner.web_options.should_prevent_default)(&egui::Event::PointerGone);
|
(runner.web_options.should_prevent_default)(&egui::Event::PointerGone);
|
||||||
runner.input.raw.events.push(egui::Event::PointerGone);
|
runner.input.raw.events.push(egui::Event::PointerGone);
|
||||||
|
|
||||||
push_touches(runner, egui::TouchPhase::End, &event);
|
push_touches(runner, egui::TouchPhase::End, &event);
|
||||||
|
|
||||||
runner.needs_repaint.repaint_asap();
|
runner.needs_repaint.repaint_asap();
|
||||||
|
|
||||||
// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
|
// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
|
||||||
if should_stop_propagation {
|
if should_stop_propagation {
|
||||||
event.stop_propagation();
|
event.stop_propagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
if should_prevent_default {
|
if should_prevent_default {
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix virtual keyboard IOS
|
// Fix virtual keyboard IOS
|
||||||
// Need call focus at the same time of event
|
// Need call focus at the same time of event
|
||||||
if runner.text_agent.has_focus() {
|
if runner.text_agent.has_focus() {
|
||||||
runner.text_agent.set_focus(false);
|
runner.text_agent.set_focus(false);
|
||||||
runner.text_agent.set_focus(true);
|
runner.text_agent.set_focus(true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
//! [`egui`] bindings for web apps (compiling to WASM).
|
//! [`egui`] bindings for web apps (compiling to WASM).
|
||||||
|
|
||||||
#![allow(clippy::missing_errors_doc)] // So many `-> Result<_, JsValue>`
|
#![expect(clippy::missing_errors_doc)] // So many `-> Result<_, JsValue>`
|
||||||
|
#![expect(clippy::unwrap_used)] // TODO(emilk): remove unwraps
|
||||||
|
|
||||||
mod app_runner;
|
mod app_runner;
|
||||||
mod backend;
|
mod backend;
|
||||||
@@ -145,18 +146,18 @@ fn canvas_content_rect(canvas: &web_sys::HtmlCanvasElement) -> egui::Rect {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// We need to subtract padding and border:
|
// We need to subtract padding and border:
|
||||||
if let Some(window) = web_sys::window() {
|
if let Some(window) = web_sys::window()
|
||||||
if let Ok(Some(style)) = window.get_computed_style(canvas) {
|
&& let Ok(Some(style)) = window.get_computed_style(canvas)
|
||||||
let get_property = |name: &str| -> Option<f32> {
|
{
|
||||||
let property = style.get_property_value(name).ok()?;
|
let get_property = |name: &str| -> Option<f32> {
|
||||||
property.trim_end_matches("px").parse::<f32>().ok()
|
let property = style.get_property_value(name).ok()?;
|
||||||
};
|
property.trim_end_matches("px").parse::<f32>().ok()
|
||||||
|
};
|
||||||
|
|
||||||
rect.min.x += get_property("padding-left").unwrap_or_default();
|
rect.min.x += get_property("padding-left").unwrap_or_default();
|
||||||
rect.min.y += get_property("padding-top").unwrap_or_default();
|
rect.min.y += get_property("padding-top").unwrap_or_default();
|
||||||
rect.max.x -= get_property("padding-right").unwrap_or_default();
|
rect.max.x -= get_property("padding-right").unwrap_or_default();
|
||||||
rect.max.y -= get_property("padding-bottom").unwrap_or_default();
|
rect.max.y -= get_property("padding-bottom").unwrap_or_default();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rect
|
rect
|
||||||
@@ -284,8 +285,8 @@ fn create_clipboard_item(mime: &str, bytes: &[u8]) -> Result<web_sys::ClipboardI
|
|||||||
|
|
||||||
let items = js_sys::Object::new();
|
let items = js_sys::Object::new();
|
||||||
|
|
||||||
// SAFETY: I hope so
|
|
||||||
#[expect(unsafe_code, unused_unsafe)] // Weird false positive
|
#[expect(unsafe_code, unused_unsafe)] // Weird false positive
|
||||||
|
// SAFETY: I hope so
|
||||||
unsafe {
|
unsafe {
|
||||||
js_sys::Reflect::set(&items, &JsValue::from_str(mime), &blob)?
|
js_sys::Reflect::set(&items, &JsValue::from_str(mime), &blob)?
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ impl log::Log for WebLogger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn log(&self, record: &log::Record<'_>) {
|
fn log(&self, record: &log::Record<'_>) {
|
||||||
#![allow(clippy::match_same_arms)]
|
#![expect(clippy::match_same_arms)]
|
||||||
|
|
||||||
if !self.enabled(record.metadata()) {
|
if !self.enabled(record.metadata()) {
|
||||||
return;
|
return;
|
||||||
@@ -110,7 +110,7 @@ mod console {
|
|||||||
/// * `tokio-1.24.1/src/runtime/runtime.rs`
|
/// * `tokio-1.24.1/src/runtime/runtime.rs`
|
||||||
/// * `rerun/src/main.rs`
|
/// * `rerun/src/main.rs`
|
||||||
/// * `core/src/ops/function.rs`
|
/// * `core/src/ops/function.rs`
|
||||||
#[allow(dead_code, clippy::allow_attributes)] // only used on web and in tests
|
#[allow(clippy::allow_attributes, dead_code)] // only used on web and in tests
|
||||||
fn shorten_file_path(file_path: &str) -> &str {
|
fn shorten_file_path(file_path: &str) -> &str {
|
||||||
if let Some(i) = file_path.rfind("/src/") {
|
if let Some(i) = file_path.rfind("/src/") {
|
||||||
if let Some(prev_slash) = file_path[..i].rfind('/') {
|
if let Some(prev_slash) = file_path[..i].rfind('/') {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ impl WebPainterGlow {
|
|||||||
let (gl, shader_prefix) =
|
let (gl, shader_prefix) =
|
||||||
init_glow_context_from_canvas(&canvas, options.webgl_context_option)?;
|
init_glow_context_from_canvas(&canvas, options.webgl_context_option)?;
|
||||||
|
|
||||||
#[allow(clippy::arc_with_non_send_sync, clippy::allow_attributes)] // For wasm
|
#[allow(clippy::allow_attributes, clippy::arc_with_non_send_sync)] // For wasm
|
||||||
let gl = std::sync::Arc::new(gl);
|
let gl = std::sync::Arc::new(gl);
|
||||||
|
|
||||||
let painter = egui_glow::Painter::new(gl, shader_prefix, None, options.dithering)
|
let painter = egui_glow::Painter::new(gl, shader_prefix, None, options.dithering)
|
||||||
@@ -91,7 +91,7 @@ impl WebPainter for WebPainterGlow {
|
|||||||
for data in data {
|
for data in data {
|
||||||
events.push(Event::Screenshot {
|
events.push(Event::Screenshot {
|
||||||
viewport_id: ViewportId::default(),
|
viewport_id: ViewportId::default(),
|
||||||
image: image.clone(),
|
image: Arc::clone(&image),
|
||||||
user_data: data,
|
user_data: data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -192,17 +192,13 @@ fn is_safari_and_webkit_gtk(gl: &web_sys::WebGlRenderingContext) -> bool {
|
|||||||
.get_extension("WEBGL_debug_renderer_info")
|
.get_extension("WEBGL_debug_renderer_info")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
&& let Ok(renderer) =
|
||||||
if let Ok(renderer) =
|
|
||||||
gl.get_parameter(web_sys::WebglDebugRendererInfo::UNMASKED_RENDERER_WEBGL)
|
gl.get_parameter(web_sys::WebglDebugRendererInfo::UNMASKED_RENDERER_WEBGL)
|
||||||
{
|
&& let Some(renderer) = renderer.as_string()
|
||||||
if let Some(renderer) = renderer.as_string() {
|
&& renderer.contains("Apple")
|
||||||
if renderer.contains("Apple") {
|
{
|
||||||
return true;
|
true
|
||||||
}
|
} else {
|
||||||
}
|
false
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ impl WebPainterWgpu {
|
|||||||
surface_configuration,
|
surface_configuration,
|
||||||
depth_stencil_format,
|
depth_stencil_format,
|
||||||
depth_texture_view: None,
|
depth_texture_view: None,
|
||||||
on_surface_error: options.wgpu_options.on_surface_error.clone(),
|
on_surface_error: Arc::clone(&options.wgpu_options.on_surface_error) as _,
|
||||||
screen_capture_state: None,
|
screen_capture_state: None,
|
||||||
capture_tx,
|
capture_tx,
|
||||||
capture_rx,
|
capture_rx,
|
||||||
@@ -282,14 +282,12 @@ impl WebPainter for WebPainterWgpu {
|
|||||||
|
|
||||||
let mut capture_buffer = None;
|
let mut capture_buffer = None;
|
||||||
|
|
||||||
if capture {
|
if capture && let Some(capture_state) = &mut self.screen_capture_state {
|
||||||
if let Some(capture_state) = &mut self.screen_capture_state {
|
capture_buffer = Some(capture_state.copy_textures(
|
||||||
capture_buffer = Some(capture_state.copy_textures(
|
&render_state.device,
|
||||||
&render_state.device,
|
&output_frame,
|
||||||
&output_frame,
|
&mut encoder,
|
||||||
&mut encoder,
|
));
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((output_frame, capture_buffer))
|
Some((output_frame, capture_buffer))
|
||||||
@@ -301,16 +299,16 @@ impl WebPainter for WebPainterWgpu {
|
|||||||
.submit(user_cmd_bufs.into_iter().chain([encoder.finish()]));
|
.submit(user_cmd_bufs.into_iter().chain([encoder.finish()]));
|
||||||
|
|
||||||
if let Some((frame, capture_buffer)) = frame_and_capture_buffer {
|
if let Some((frame, capture_buffer)) = frame_and_capture_buffer {
|
||||||
if let Some(capture_buffer) = capture_buffer {
|
if let Some(capture_buffer) = capture_buffer
|
||||||
if let Some(capture_state) = &self.screen_capture_state {
|
&& let Some(capture_state) = &self.screen_capture_state
|
||||||
capture_state.read_screen_rgba(
|
{
|
||||||
self.ctx.clone(),
|
capture_state.read_screen_rgba(
|
||||||
capture_buffer,
|
self.ctx.clone(),
|
||||||
capture_data,
|
capture_buffer,
|
||||||
self.capture_tx.clone(),
|
capture_data,
|
||||||
ViewportId::ROOT,
|
self.capture_tx.clone(),
|
||||||
);
|
ViewportId::ROOT,
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.present();
|
frame.present();
|
||||||
@@ -336,7 +334,7 @@ impl WebPainter for WebPainterWgpu {
|
|||||||
events.push(Event::Screenshot {
|
events.push(Event::Screenshot {
|
||||||
viewport_id,
|
viewport_id,
|
||||||
user_data: data,
|
user_data: data,
|
||||||
image: screenshot.clone(),
|
image: Arc::clone(&screenshot),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ impl CaptureState {
|
|||||||
// It would be more efficient to reuse the Buffer, e.g. via some kind of ring buffer, but
|
// It would be more efficient to reuse the Buffer, e.g. via some kind of ring buffer, but
|
||||||
// for most screenshot use cases this should be fine. When taking many screenshots (e.g. for a video)
|
// for most screenshot use cases this should be fine. When taking many screenshots (e.g. for a video)
|
||||||
// it might make sense to revisit this and implement a more efficient solution.
|
// it might make sense to revisit this and implement a more efficient solution.
|
||||||
#[allow(clippy::arc_with_non_send_sync, clippy::allow_attributes)] // For wasm
|
#[allow(clippy::allow_attributes, clippy::arc_with_non_send_sync)] // For wasm
|
||||||
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
label: Some("egui_screen_capture_buffer"),
|
label: Some("egui_screen_capture_buffer"),
|
||||||
size: (self.padding.padded_bytes_per_row * self.texture.height()) as u64,
|
size: (self.padding.padded_bytes_per_row * self.texture.height()) as u64,
|
||||||
@@ -186,9 +186,9 @@ impl CaptureState {
|
|||||||
tx: CaptureSender,
|
tx: CaptureSender,
|
||||||
viewport_id: ViewportId,
|
viewport_id: ViewportId,
|
||||||
) {
|
) {
|
||||||
#[allow(clippy::arc_with_non_send_sync, clippy::allow_attributes)] // For wasm
|
#[allow(clippy::allow_attributes, clippy::arc_with_non_send_sync)] // For wasm
|
||||||
let buffer = Arc::new(buffer);
|
let buffer = Arc::new(buffer);
|
||||||
let buffer_clone = buffer.clone();
|
let buffer_clone = Arc::clone(&buffer);
|
||||||
let buffer_slice = buffer_clone.slice(..);
|
let buffer_slice = buffer_clone.slice(..);
|
||||||
let format = self.texture.format();
|
let format = self.texture.format();
|
||||||
let tex_extent = self.texture.size();
|
let tex_extent = self.texture.size();
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
#![doc = document_features::document_features!()]
|
#![doc = document_features::document_features!()]
|
||||||
//!
|
//!
|
||||||
|
|
||||||
#![allow(unsafe_code)]
|
|
||||||
|
|
||||||
pub use wgpu;
|
pub use wgpu;
|
||||||
|
|
||||||
/// Low-level painting of [`egui`](https://github.com/emilk/egui) on [`wgpu`].
|
/// Low-level painting of [`egui`](https://github.com/emilk/egui) on [`wgpu`].
|
||||||
@@ -247,7 +245,7 @@ impl RenderState {
|
|||||||
|
|
||||||
// On wasm, depending on feature flags, wgpu objects may or may not implement sync.
|
// On wasm, depending on feature flags, wgpu objects may or may not implement sync.
|
||||||
// It doesn't make sense to switch to Rc for that special usecase, so simply disable the lint.
|
// It doesn't make sense to switch to Rc for that special usecase, so simply disable the lint.
|
||||||
#[allow(clippy::arc_with_non_send_sync, clippy::allow_attributes)] // For wasm
|
#[allow(clippy::allow_attributes, clippy::arc_with_non_send_sync)] // For wasm
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
adapter,
|
adapter,
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![allow(unsafe_code)]
|
#![expect(clippy::unwrap_used)] // TODO(emilk): avoid unwraps
|
||||||
|
|
||||||
use std::{borrow::Cow, num::NonZeroU64, ops::Range};
|
use std::{borrow::Cow, num::NonZeroU64, ops::Range};
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ impl WgpuSetup {
|
|||||||
pub async fn new_instance(&self) -> wgpu::Instance {
|
pub async fn new_instance(&self) -> wgpu::Instance {
|
||||||
match self {
|
match self {
|
||||||
Self::CreateNew(create_new) => {
|
Self::CreateNew(create_new) => {
|
||||||
#[allow(unused_mut, clippy::allow_attributes)]
|
#[allow(clippy::allow_attributes, unused_mut)]
|
||||||
let mut backends = create_new.instance_descriptor.backends;
|
let mut backends = create_new.instance_descriptor.backends;
|
||||||
|
|
||||||
// Don't try WebGPU if we're not in a secure context.
|
// Don't try WebGPU if we're not in a secure context.
|
||||||
@@ -134,7 +134,7 @@ impl Clone for WgpuSetupCreateNew {
|
|||||||
instance_descriptor: self.instance_descriptor.clone(),
|
instance_descriptor: self.instance_descriptor.clone(),
|
||||||
power_preference: self.power_preference,
|
power_preference: self.power_preference,
|
||||||
native_adapter_selector: self.native_adapter_selector.clone(),
|
native_adapter_selector: self.native_adapter_selector.clone(),
|
||||||
device_descriptor: self.device_descriptor.clone(),
|
device_descriptor: Arc::clone(&self.device_descriptor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#![allow(clippy::missing_errors_doc)]
|
#![expect(clippy::missing_errors_doc)]
|
||||||
#![allow(clippy::undocumented_unsafe_blocks)]
|
#![expect(clippy::undocumented_unsafe_blocks)]
|
||||||
|
#![expect(clippy::unwrap_used)] // TODO(emilk): avoid unwraps
|
||||||
|
#![expect(unsafe_code)]
|
||||||
|
|
||||||
use crate::{RenderState, SurfaceErrorAction, WgpuConfiguration, renderer};
|
use crate::{RenderState, SurfaceErrorAction, WgpuConfiguration, renderer};
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -629,7 +631,7 @@ impl Painter {
|
|||||||
events.push(Event::Screenshot {
|
events.push(Event::Screenshot {
|
||||||
viewport_id,
|
viewport_id,
|
||||||
user_data: data,
|
user_data: data,
|
||||||
image: screenshot.clone(),
|
image: Arc::clone(&screenshot),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ fn init_arboard() -> Option<arboard::Clipboard> {
|
|||||||
fn init_smithay_clipboard(
|
fn init_smithay_clipboard(
|
||||||
raw_display_handle: Option<RawDisplayHandle>,
|
raw_display_handle: Option<RawDisplayHandle>,
|
||||||
) -> Option<smithay_clipboard::Clipboard> {
|
) -> Option<smithay_clipboard::Clipboard> {
|
||||||
#![allow(clippy::undocumented_unsafe_blocks)]
|
#![expect(clippy::undocumented_unsafe_blocks)]
|
||||||
|
|
||||||
profiling::function_scope!();
|
profiling::function_scope!();
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
|
#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
|
||||||
//!
|
//!
|
||||||
|
|
||||||
#![allow(clippy::manual_range_contains)]
|
#![expect(clippy::manual_range_contains)]
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
pub use accesskit_winit;
|
pub use accesskit_winit;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ mod ios {
|
|||||||
|
|
||||||
let app = UIApplication::sharedApplication(main_thread_marker);
|
let app = UIApplication::sharedApplication(main_thread_marker);
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[expect(unsafe_code)]
|
||||||
unsafe {
|
unsafe {
|
||||||
// Look for the first window scene that's in the foreground
|
// Look for the first window scene that's in the foreground
|
||||||
for scene in app.connectedScenes() {
|
for scene in app.connectedScenes() {
|
||||||
|
|||||||
@@ -198,8 +198,7 @@ macro_rules! all_the_atoms {
|
|||||||
$($T: IntoAtoms<'a>),*
|
$($T: IntoAtoms<'a>),*
|
||||||
{
|
{
|
||||||
fn collect(self, _atoms: &mut Atoms<'a>) {
|
fn collect(self, _atoms: &mut Atoms<'a>) {
|
||||||
#[allow(clippy::allow_attributes)]
|
#[allow(clippy::allow_attributes, non_snake_case)]
|
||||||
#[allow(non_snake_case)]
|
|
||||||
let ($($T),*) = self;
|
let ($($T),*) = self;
|
||||||
$($T.collect(_atoms);)*
|
$($T.collect(_atoms);)*
|
||||||
}
|
}
|
||||||
|
|||||||
1
crates/egui/src/cache/cache_storage.rs
vendored
1
crates/egui/src/cache/cache_storage.rs
vendored
@@ -28,6 +28,7 @@ pub struct CacheStorage {
|
|||||||
|
|
||||||
impl CacheStorage {
|
impl CacheStorage {
|
||||||
pub fn cache<Cache: CacheTrait + Default>(&mut self) -> &mut Cache {
|
pub fn cache<Cache: CacheTrait + Default>(&mut self) -> &mut Cache {
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
self.caches
|
self.caches
|
||||||
.entry(std::any::TypeId::of::<Cache>())
|
.entry(std::any::TypeId::of::<Cache>())
|
||||||
.or_insert_with(|| Box::<Cache>::default())
|
.or_insert_with(|| Box::<Cache>::default())
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ impl Area {
|
|||||||
|
|
||||||
/// Set the [`UiStackInfo`] of the area's [`Ui`].
|
/// Set the [`UiStackInfo`] of the area's [`Ui`].
|
||||||
///
|
///
|
||||||
/// Default to [`UiStackInfo::new(UiKind::GenericArea)`].
|
/// Default to [`UiStackInfo`] with kind [`UiKind::GenericArea`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn info(mut self, info: UiStackInfo) -> Self {
|
pub fn info(mut self, info: UiStackInfo) -> Self {
|
||||||
self.info = info;
|
self.info = info;
|
||||||
@@ -459,7 +459,7 @@ impl Area {
|
|||||||
state.pivot_pos = Some(new_pos);
|
state.pivot_pos = Some(new_pos);
|
||||||
}
|
}
|
||||||
state.pivot_pos.get_or_insert_with(|| {
|
state.pivot_pos.get_or_insert_with(|| {
|
||||||
default_pos.unwrap_or_else(|| automatic_area_position(ctx, layer_id))
|
default_pos.unwrap_or_else(|| automatic_area_position(ctx, constrain_rect, layer_id))
|
||||||
});
|
});
|
||||||
state.interactable = interactable;
|
state.interactable = interactable;
|
||||||
|
|
||||||
@@ -523,6 +523,7 @@ impl Area {
|
|||||||
enabled,
|
enabled,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
Default::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Used to prevent drift
|
// Used to prevent drift
|
||||||
@@ -699,7 +700,7 @@ fn pointer_pressed_on_area(ctx: &Context, layer_id: LayerId) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn automatic_area_position(ctx: &Context, layer_id: LayerId) -> Pos2 {
|
fn automatic_area_position(ctx: &Context, constrain_rect: Rect, layer_id: LayerId) -> Pos2 {
|
||||||
let mut existing: Vec<Rect> = ctx.memory(|mem| {
|
let mut existing: Vec<Rect> = ctx.memory(|mem| {
|
||||||
mem.areas()
|
mem.areas()
|
||||||
.visible_windows()
|
.visible_windows()
|
||||||
@@ -710,13 +711,9 @@ fn automatic_area_position(ctx: &Context, layer_id: LayerId) -> Pos2 {
|
|||||||
});
|
});
|
||||||
existing.sort_by_key(|r| r.left().round() as i32);
|
existing.sort_by_key(|r| r.left().round() as i32);
|
||||||
|
|
||||||
// NOTE: for the benefit of the egui demo, we position the windows so they don't
|
|
||||||
// cover the side panels, which means we use `available_rect` here instead of `constrain_rect` or `screen_rect`.
|
|
||||||
let available_rect = ctx.globally_available_rect();
|
|
||||||
|
|
||||||
let spacing = 16.0;
|
let spacing = 16.0;
|
||||||
let left = available_rect.left() + spacing;
|
let left = constrain_rect.left() + spacing;
|
||||||
let top = available_rect.top() + spacing;
|
let top = constrain_rect.top() + spacing;
|
||||||
|
|
||||||
if existing.is_empty() {
|
if existing.is_empty() {
|
||||||
return pos2(left, top);
|
return pos2(left, top);
|
||||||
@@ -726,6 +723,7 @@ fn automatic_area_position(ctx: &Context, layer_id: LayerId) -> Pos2 {
|
|||||||
let mut column_bbs = vec![existing[0]];
|
let mut column_bbs = vec![existing[0]];
|
||||||
|
|
||||||
for &rect in &existing {
|
for &rect in &existing {
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
let current_column_bb = column_bbs.last_mut().unwrap();
|
let current_column_bb = column_bbs.last_mut().unwrap();
|
||||||
if rect.left() < current_column_bb.right() {
|
if rect.left() < current_column_bb.right() {
|
||||||
// same column
|
// same column
|
||||||
@@ -750,14 +748,15 @@ fn automatic_area_position(ctx: &Context, layer_id: LayerId) -> Pos2 {
|
|||||||
|
|
||||||
// Find first column with some available space at the bottom of it:
|
// Find first column with some available space at the bottom of it:
|
||||||
for col_bb in &column_bbs {
|
for col_bb in &column_bbs {
|
||||||
if col_bb.bottom() < available_rect.center().y {
|
if col_bb.bottom() < constrain_rect.center().y {
|
||||||
return pos2(col_bb.left(), col_bb.bottom() + spacing);
|
return pos2(col_bb.left(), col_bb.bottom() + spacing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maybe we can fit a new column?
|
// Maybe we can fit a new column?
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
let rightmost = column_bbs.last().unwrap().right();
|
let rightmost = column_bbs.last().unwrap().right();
|
||||||
if rightmost + 200.0 < available_rect.right() {
|
if rightmost + 200.0 < constrain_rect.right() {
|
||||||
return pos2(rightmost + spacing, top);
|
return pos2(rightmost + spacing, top);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -336,7 +336,7 @@ impl Frame {
|
|||||||
impl Frame {
|
impl Frame {
|
||||||
/// How much extra space the frame uses up compared to the content.
|
/// How much extra space the frame uses up compared to the content.
|
||||||
///
|
///
|
||||||
/// [`Self::inner_margin`] + [`Self.stroke`]`.width` + [`Self::outer_margin`].
|
/// [`Self::inner_margin`] + [`Self::stroke`]`.width` + [`Self::outer_margin`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn total_margin(&self) -> MarginF32 {
|
pub fn total_margin(&self) -> MarginF32 {
|
||||||
MarginF32::from(self.inner_margin)
|
MarginF32::from(self.inner_margin)
|
||||||
|
|||||||
@@ -444,11 +444,8 @@ impl SubMenu {
|
|||||||
let mut menu_config = self.config.unwrap_or_else(|| parent_config.clone());
|
let mut menu_config = self.config.unwrap_or_else(|| parent_config.clone());
|
||||||
menu_config.bar = false;
|
menu_config.bar = false;
|
||||||
|
|
||||||
let menu_root_response = ui
|
#[expect(clippy::unwrap_used)] // Since we are a child of that ui, this should always exist
|
||||||
.ctx()
|
let menu_root_response = ui.ctx().read_response(menu_id).unwrap();
|
||||||
.read_response(menu_id)
|
|
||||||
// Since we are a child of that ui, this should always exist
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let hover_pos = ui.ctx().pointer_hover_pos();
|
let hover_pos = ui.ctx().pointer_hover_pos();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//! Old and deprecated API for popups. Use [`Popup`] instead.
|
//! Old and deprecated API for popups. Use [`Popup`] instead.
|
||||||
#![allow(deprecated)]
|
#![expect(deprecated)]
|
||||||
|
|
||||||
use crate::containers::tooltip::Tooltip;
|
use crate::containers::tooltip::Tooltip;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|||||||
@@ -255,9 +255,7 @@ impl<'a> PanelSizer<'a> {
|
|||||||
let side = self.panel.side;
|
let side = self.panel.side;
|
||||||
let size_range = self.panel.size_range;
|
let size_range = self.panel.size_range;
|
||||||
|
|
||||||
if is_resizing && pointer.is_some() {
|
if is_resizing && let Some(pointer) = pointer {
|
||||||
let pointer = pointer.unwrap();
|
|
||||||
|
|
||||||
match side {
|
match side {
|
||||||
PanelSide::Vertical(side) => {
|
PanelSide::Vertical(side) => {
|
||||||
self.size = (pointer.x - side.side_x(self.panel_rect)).abs();
|
self.size = (pointer.x - side.side_x(self.panel_rect)).abs();
|
||||||
@@ -760,8 +758,10 @@ impl Panel {
|
|||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,
|
add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,
|
||||||
) -> InnerResponse<R> {
|
) -> InnerResponse<R> {
|
||||||
|
#![expect(deprecated)]
|
||||||
|
|
||||||
let side = self.side;
|
let side = self.side;
|
||||||
let available_rect = ctx.globally_available_rect();
|
let available_rect = ctx.available_rect();
|
||||||
let mut panel_ui = Ui::new(
|
let mut panel_ui = Ui::new(
|
||||||
ctx.clone(),
|
ctx.clone(),
|
||||||
self.id,
|
self.id,
|
||||||
@@ -809,9 +809,7 @@ impl Panel {
|
|||||||
let resize_id = self.id.with("__resize");
|
let resize_id = self.id.with("__resize");
|
||||||
let resize_response = ui.ctx().read_response(resize_id);
|
let resize_response = ui.ctx().read_response(resize_id);
|
||||||
|
|
||||||
if resize_response.is_some() {
|
if let Some(resize_response) = resize_response {
|
||||||
let resize_response = resize_response.unwrap();
|
|
||||||
|
|
||||||
// NOTE(sharky98): The original code was initializing to
|
// NOTE(sharky98): The original code was initializing to
|
||||||
// false first, but it doesn't seem necessary.
|
// false first, but it doesn't seem necessary.
|
||||||
let is_resizing = resize_response.dragged();
|
let is_resizing = resize_response.dragged();
|
||||||
@@ -1044,6 +1042,7 @@ impl CentralPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Show the panel at the top level.
|
/// Show the panel at the top level.
|
||||||
|
#[deprecated = "Use show_inside() instead"]
|
||||||
pub fn show<R>(
|
pub fn show<R>(
|
||||||
self,
|
self,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
@@ -1058,6 +1057,8 @@ impl CentralPanel {
|
|||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,
|
add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,
|
||||||
) -> InnerResponse<R> {
|
) -> InnerResponse<R> {
|
||||||
|
#![expect(deprecated)]
|
||||||
|
|
||||||
let id = Id::new((ctx.viewport_id(), "central_panel"));
|
let id = Id::new((ctx.viewport_id(), "central_panel"));
|
||||||
|
|
||||||
let mut panel_ui = Ui::new(
|
let mut panel_ui = Ui::new(
|
||||||
@@ -1065,7 +1066,7 @@ impl CentralPanel {
|
|||||||
id,
|
id,
|
||||||
UiBuilder::new()
|
UiBuilder::new()
|
||||||
.layer_id(LayerId::background())
|
.layer_id(LayerId::background())
|
||||||
.max_rect(ctx.globally_available_rect().round_ui()),
|
.max_rect(ctx.available_rect().round_ui()),
|
||||||
);
|
);
|
||||||
panel_ui.set_clip_rect(ctx.content_rect());
|
panel_ui.set_clip_rect(ctx.content_rect());
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! See [`ScrollArea`] for docs.
|
//! See [`ScrollArea`] for docs.
|
||||||
|
|
||||||
#![allow(clippy::needless_range_loop)]
|
#![expect(clippy::needless_range_loop)]
|
||||||
|
|
||||||
use std::ops::{Add, AddAssign, BitOr, BitOrAssign};
|
use std::ops::{Add, AddAssign, BitOr, BitOrAssign};
|
||||||
|
|
||||||
@@ -9,7 +9,8 @@ use epaint::Margin;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Response, Sense, Ui, UiBuilder,
|
Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Response, Sense, Ui, UiBuilder,
|
||||||
UiKind, UiStackInfo, Vec2, Vec2b, emath, epaint, lerp, pass_state, pos2, remap, remap_clamp,
|
UiKind, UiStackInfo, Vec2, Vec2b, WidgetInfo, emath, epaint, lerp, pass_state, pos2, remap,
|
||||||
|
remap_clamp,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
@@ -1241,46 +1242,17 @@ impl Prepared {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let interact_id = id.with(d);
|
||||||
|
|
||||||
// Margin on either side of the scroll bar:
|
// Margin on either side of the scroll bar:
|
||||||
let inner_margin = show_factor * scroll_style.bar_inner_margin;
|
let inner_margin = show_factor * scroll_style.bar_inner_margin;
|
||||||
let outer_margin = show_factor * scroll_style.bar_outer_margin;
|
let outer_margin = show_factor * scroll_style.bar_outer_margin;
|
||||||
|
|
||||||
// top/bottom of a horizontal scroll (d==0).
|
// bottom of a horizontal scroll (d==0).
|
||||||
// left/rigth of a vertical scroll (d==1).
|
// right of a vertical scroll (d==1).
|
||||||
let mut cross = if scroll_style.floating {
|
let mut max_cross = outer_rect.max[1 - d] - outer_margin;
|
||||||
// The bounding rect of a fully visible bar.
|
|
||||||
// When we hover this area, we should show the full bar:
|
|
||||||
let max_bar_rect = if d == 0 {
|
|
||||||
outer_rect.with_min_y(outer_rect.max.y - outer_margin - scroll_style.bar_width)
|
|
||||||
} else {
|
|
||||||
outer_rect.with_min_x(outer_rect.max.x - outer_margin - scroll_style.bar_width)
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_hovering_bar_area = is_hovering_outer_rect
|
if ui.clip_rect().max[1 - d] - outer_margin < max_cross {
|
||||||
&& ui.rect_contains_pointer(max_bar_rect)
|
|
||||||
&& !is_dragging_background
|
|
||||||
|| state.scroll_bar_interaction[d];
|
|
||||||
|
|
||||||
let is_hovering_bar_area_t = ui
|
|
||||||
.ctx()
|
|
||||||
.animate_bool_responsive(id.with((d, "bar_hover")), is_hovering_bar_area);
|
|
||||||
|
|
||||||
let width = show_factor
|
|
||||||
* lerp(
|
|
||||||
scroll_style.floating_width..=scroll_style.bar_width,
|
|
||||||
is_hovering_bar_area_t,
|
|
||||||
);
|
|
||||||
|
|
||||||
let max_cross = outer_rect.max[1 - d] - outer_margin;
|
|
||||||
let min_cross = max_cross - width;
|
|
||||||
Rangef::new(min_cross, max_cross)
|
|
||||||
} else {
|
|
||||||
let min_cross = inner_rect.max[1 - d] + inner_margin;
|
|
||||||
let max_cross = outer_rect.max[1 - d] - outer_margin;
|
|
||||||
Rangef::new(min_cross, max_cross)
|
|
||||||
};
|
|
||||||
|
|
||||||
if ui.clip_rect().max[1 - d] < cross.max + outer_margin {
|
|
||||||
// Move the scrollbar so it is visible. This is needed in some cases.
|
// Move the scrollbar so it is visible. This is needed in some cases.
|
||||||
// For instance:
|
// For instance:
|
||||||
// * When we have a vertical-only scroll area in a top level panel,
|
// * When we have a vertical-only scroll area in a top level panel,
|
||||||
@@ -1290,21 +1262,59 @@ impl Prepared {
|
|||||||
// is outside the clip rectangle.
|
// is outside the clip rectangle.
|
||||||
// Really this should use the tighter clip_rect that ignores clip_rect_margin, but we don't store that.
|
// Really this should use the tighter clip_rect that ignores clip_rect_margin, but we don't store that.
|
||||||
// clip_rect_margin is quite a hack. It would be nice to get rid of it.
|
// clip_rect_margin is quite a hack. It would be nice to get rid of it.
|
||||||
let width = cross.max - cross.min;
|
max_cross = ui.clip_rect().max[1 - d] - outer_margin;
|
||||||
cross.max = ui.clip_rect().max[1 - d] - outer_margin;
|
|
||||||
cross.min = cross.max - width;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let outer_scroll_bar_rect = if d == 0 {
|
let full_width = scroll_style.bar_width;
|
||||||
Rect::from_min_max(
|
|
||||||
pos2(scroll_bar_rect.left(), cross.min),
|
// The bounding rect of a fully visible bar.
|
||||||
pos2(scroll_bar_rect.right(), cross.max),
|
// When we hover this area, we should show the full bar:
|
||||||
)
|
let max_bar_rect = if d == 0 {
|
||||||
|
outer_rect.with_min_y(max_cross - full_width)
|
||||||
} else {
|
} else {
|
||||||
Rect::from_min_max(
|
outer_rect.with_min_x(max_cross - full_width)
|
||||||
pos2(cross.min, scroll_bar_rect.top()),
|
};
|
||||||
pos2(cross.max, scroll_bar_rect.bottom()),
|
|
||||||
)
|
let sense = if scroll_source.scroll_bar && ui.is_enabled() {
|
||||||
|
Sense::click_and_drag()
|
||||||
|
} else {
|
||||||
|
Sense::hover()
|
||||||
|
};
|
||||||
|
|
||||||
|
// We always sense interaction with the full width, even if we antimate it growing/shrinking.
|
||||||
|
// This is to present a more consistent target for our hit test code,
|
||||||
|
// and to avoid producing jitter in "thin widget" heuristics there.
|
||||||
|
// Also: it make sense to detect any hover where the scroll bar _will_ be.
|
||||||
|
let response = ui.interact(max_bar_rect, interact_id, sense);
|
||||||
|
|
||||||
|
response.widget_info(|| WidgetInfo::new(crate::WidgetType::ScrollBar));
|
||||||
|
|
||||||
|
// top/bottom of a horizontal scroll (d==0).
|
||||||
|
// left/rigth of a vertical scroll (d==1).
|
||||||
|
let cross = if scroll_style.floating {
|
||||||
|
let is_hovering_bar_area = response.hovered() || state.scroll_bar_interaction[d];
|
||||||
|
|
||||||
|
let is_hovering_bar_area_t = ui
|
||||||
|
.ctx()
|
||||||
|
.animate_bool_responsive(id.with((d, "bar_hover")), is_hovering_bar_area);
|
||||||
|
|
||||||
|
let width = show_factor
|
||||||
|
* lerp(
|
||||||
|
scroll_style.floating_width..=full_width,
|
||||||
|
is_hovering_bar_area_t,
|
||||||
|
);
|
||||||
|
|
||||||
|
let min_cross = max_cross - width;
|
||||||
|
Rangef::new(min_cross, max_cross)
|
||||||
|
} else {
|
||||||
|
let min_cross = inner_rect.max[1 - d] + inner_margin;
|
||||||
|
Rangef::new(min_cross, max_cross)
|
||||||
|
};
|
||||||
|
|
||||||
|
let outer_scroll_bar_rect = if d == 0 {
|
||||||
|
Rect::from_x_y_ranges(scroll_bar_rect.x_range(), cross)
|
||||||
|
} else {
|
||||||
|
Rect::from_x_y_ranges(cross, scroll_bar_rect.y_range())
|
||||||
};
|
};
|
||||||
|
|
||||||
let from_content = |content| {
|
let from_content = |content| {
|
||||||
@@ -1344,14 +1354,6 @@ impl Prepared {
|
|||||||
|
|
||||||
let handle_rect = calculate_handle_rect(d, &state.offset);
|
let handle_rect = calculate_handle_rect(d, &state.offset);
|
||||||
|
|
||||||
let interact_id = id.with(d);
|
|
||||||
let sense = if scroll_source.scroll_bar && ui.is_enabled() {
|
|
||||||
Sense::click_and_drag()
|
|
||||||
} else {
|
|
||||||
Sense::hover()
|
|
||||||
};
|
|
||||||
let response = ui.interact(outer_scroll_bar_rect, interact_id, sense);
|
|
||||||
|
|
||||||
state.scroll_bar_interaction[d] = response.hovered() || response.dragged();
|
state.scroll_bar_interaction[d] = response.hovered() || response.dragged();
|
||||||
|
|
||||||
if let Some(pointer_pos) = response.interact_pointer_pos() {
|
if let Some(pointer_pos) = response.interact_pointer_pos() {
|
||||||
|
|||||||
@@ -475,11 +475,11 @@ impl Window<'_> {
|
|||||||
fade_out,
|
fade_out,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let header_color = frame.map_or_else(
|
let style = ctx.global_style();
|
||||||
|| ctx.global_style().visuals.widgets.open.weak_bg_fill,
|
|
||||||
|f| f.fill,
|
let header_color =
|
||||||
);
|
frame.map_or_else(|| style.visuals.widgets.open.weak_bg_fill, |f| f.fill);
|
||||||
let mut window_frame = frame.unwrap_or_else(|| Frame::window(&ctx.global_style()));
|
let mut window_frame = frame.unwrap_or_else(|| Frame::window(&style));
|
||||||
|
|
||||||
let is_explicitly_closed = matches!(open, Some(false));
|
let is_explicitly_closed = matches!(open, Some(false));
|
||||||
let is_open = !is_explicitly_closed || ctx.memory(|mem| mem.everything_is_visible());
|
let is_open = !is_explicitly_closed || ctx.memory(|mem| mem.everything_is_visible());
|
||||||
@@ -511,7 +511,6 @@ impl Window<'_> {
|
|||||||
|
|
||||||
// Calculate roughly how much larger the full window inner size is compared to the content rect
|
// Calculate roughly how much larger the full window inner size is compared to the content rect
|
||||||
let (title_bar_height_with_margin, title_content_spacing) = if with_title_bar {
|
let (title_bar_height_with_margin, title_content_spacing) = if with_title_bar {
|
||||||
let style = ctx.global_style();
|
|
||||||
let title_bar_inner_height = ctx
|
let title_bar_inner_height = ctx
|
||||||
.fonts_mut(|fonts| title.font_height(fonts, &style))
|
.fonts_mut(|fonts| title.font_height(fonts, &style))
|
||||||
.at_least(style.spacing.interact_size.y);
|
.at_least(style.spacing.interact_size.y);
|
||||||
@@ -542,7 +541,7 @@ impl Window<'_> {
|
|||||||
|
|
||||||
// First check for resize to avoid frame delay:
|
// First check for resize to avoid frame delay:
|
||||||
let last_frame_outer_rect = area.state().rect();
|
let last_frame_outer_rect = area.state().rect();
|
||||||
let resize_interaction = resize_interaction(
|
let resize_interaction = do_resize_interaction(
|
||||||
ctx,
|
ctx,
|
||||||
possible,
|
possible,
|
||||||
area.id(),
|
area.id(),
|
||||||
@@ -623,6 +622,17 @@ impl Window<'_> {
|
|||||||
.map_or((None, None), |ir| (Some(ir.inner), Some(ir.response)));
|
.map_or((None, None), |ir| (Some(ir.inner), Some(ir.response)));
|
||||||
|
|
||||||
let outer_rect = frame.end(&mut area_content_ui).rect;
|
let outer_rect = frame.end(&mut area_content_ui).rect;
|
||||||
|
|
||||||
|
// Do resize interaction _again_, to move their widget rectangles on TOP of the rest of the window.
|
||||||
|
let resize_interaction = do_resize_interaction(
|
||||||
|
ctx,
|
||||||
|
possible,
|
||||||
|
area.id(),
|
||||||
|
area_layer_id,
|
||||||
|
last_frame_outer_rect,
|
||||||
|
window_frame,
|
||||||
|
);
|
||||||
|
|
||||||
paint_resize_corner(
|
paint_resize_corner(
|
||||||
&area_content_ui,
|
&area_content_ui,
|
||||||
&possible,
|
&possible,
|
||||||
@@ -924,7 +934,7 @@ fn move_and_resize_window(ctx: &Context, id: Id, interaction: &ResizeInteraction
|
|||||||
Some(rect.round_ui())
|
Some(rect.round_ui())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resize_interaction(
|
fn do_resize_interaction(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
possible: PossibleInteractions,
|
possible: PossibleInteractions,
|
||||||
_accessibility_parent: Id,
|
_accessibility_parent: Id,
|
||||||
@@ -958,7 +968,16 @@ fn resize_interaction(
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
InteractOptions {
|
||||||
|
// We call this multiple times.
|
||||||
|
// First to read the result (to avoid frame delay)
|
||||||
|
// and the second time to move it to the top, above the window contents.
|
||||||
|
move_to_top: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
response.widget_info(|| WidgetInfo::new(crate::WidgetType::ResizeHandle));
|
||||||
|
|
||||||
SideResponse {
|
SideResponse {
|
||||||
hover: response.hovered(),
|
hover: response.hovered(),
|
||||||
drag: response.dragged(),
|
drag: response.dragged(),
|
||||||
@@ -967,8 +986,10 @@ fn resize_interaction(
|
|||||||
|
|
||||||
let id = Id::new(layer_id).with("edge_drag");
|
let id = Id::new(layer_id).with("edge_drag");
|
||||||
|
|
||||||
let side_grab_radius = ctx.global_style().interaction.resize_grab_radius_side;
|
let style = ctx.global_style();
|
||||||
let corner_grab_radius = ctx.global_style().interaction.resize_grab_radius_corner;
|
|
||||||
|
let side_grab_radius = style.interaction.resize_grab_radius_side;
|
||||||
|
let corner_grab_radius = style.interaction.resize_grab_radius_corner;
|
||||||
|
|
||||||
let vetrtical_rect = |a: Pos2, b: Pos2| {
|
let vetrtical_rect = |a: Pos2, b: Pos2| {
|
||||||
Rect::from_min_max(a, b).expand2(vec2(side_grab_radius, -corner_grab_radius))
|
Rect::from_min_max(a, b).expand2(vec2(side_grab_radius, -corner_grab_radius))
|
||||||
@@ -1274,7 +1295,7 @@ impl TitleBar {
|
|||||||
let text_pos = text_pos - self.title_galley.rect.min.to_vec2();
|
let text_pos = text_pos - self.title_galley.rect.min.to_vec2();
|
||||||
ui.painter().galley(
|
ui.painter().galley(
|
||||||
text_pos,
|
text_pos,
|
||||||
self.title_galley.clone(),
|
Arc::clone(&self.title_galley),
|
||||||
ui.visuals().text_color(),
|
ui.visuals().text_color(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ use crate::{
|
|||||||
ImmediateViewportRendererCallback, Key, KeyboardShortcut, Label, LayerId, Memory,
|
ImmediateViewportRendererCallback, Key, KeyboardShortcut, Label, LayerId, Memory,
|
||||||
ModifierNames, Modifiers, NumExt as _, Order, Painter, RawInput, Response, RichText,
|
ModifierNames, Modifiers, NumExt as _, Order, Painter, RawInput, Response, RichText,
|
||||||
SafeAreaInsets, ScrollArea, Sense, Style, TextStyle, TextureHandle, TextureOptions, Ui,
|
SafeAreaInsets, ScrollArea, Sense, Style, TextStyle, TextureHandle, TextureOptions, Ui,
|
||||||
ViewportBuilder, ViewportCommand, ViewportId, ViewportIdMap, ViewportIdPair, ViewportIdSet,
|
UiBuilder, ViewportBuilder, ViewportCommand, ViewportId, ViewportIdMap, ViewportIdPair,
|
||||||
ViewportOutput, Visuals, Widget as _, WidgetRect, WidgetText,
|
ViewportIdSet, ViewportOutput, Visuals, Widget as _, WidgetRect, WidgetText,
|
||||||
animation_manager::AnimationManager,
|
animation_manager::AnimationManager,
|
||||||
containers::{self, area::AreaState},
|
containers::{self, area::AreaState},
|
||||||
data::output::PlatformOutput,
|
data::output::PlatformOutput,
|
||||||
@@ -584,8 +584,8 @@ impl ContextImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accesskit_node_builder(&mut self, id: Id) -> &mut accesskit::Node {
|
fn accesskit_node_builder(&mut self, id: Id) -> Option<&mut accesskit::Node> {
|
||||||
let state = self.viewport().this_pass.accesskit_state.as_mut().unwrap();
|
let state = self.viewport().this_pass.accesskit_state.as_mut()?;
|
||||||
let builders = &mut state.nodes;
|
let builders = &mut state.nodes;
|
||||||
|
|
||||||
if let std::collections::hash_map::Entry::Vacant(entry) = builders.entry(id) {
|
if let std::collections::hash_map::Entry::Vacant(entry) = builders.entry(id) {
|
||||||
@@ -611,11 +611,11 @@ impl ContextImpl {
|
|||||||
let parent_id = find_accesskit_parent(&state.parent_map, builders, id)
|
let parent_id = find_accesskit_parent(&state.parent_map, builders, id)
|
||||||
.unwrap_or_else(crate::accesskit_root_id);
|
.unwrap_or_else(crate::accesskit_root_id);
|
||||||
|
|
||||||
let parent_builder = builders.get_mut(&parent_id).unwrap();
|
let parent_builder = builders.get_mut(&parent_id)?;
|
||||||
parent_builder.push_child(id.accesskit_id());
|
parent_builder.push_child(id.accesskit_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
builders.get_mut(&id).unwrap()
|
builders.get_mut(&id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pixels_per_point(&mut self) -> f32 {
|
fn pixels_per_point(&mut self) -> f32 {
|
||||||
@@ -793,11 +793,23 @@ impl Context {
|
|||||||
let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
|
let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
|
||||||
#[expect(deprecated)]
|
#[expect(deprecated)]
|
||||||
self.run(new_input, |ctx| {
|
self.run(new_input, |ctx| {
|
||||||
crate::CentralPanel::no_frame().show(ctx, |ui| {
|
let mut top_ui = Ui::new(
|
||||||
plugins.on_begin_pass(ui);
|
ctx.clone(),
|
||||||
run_ui(ui);
|
Id::new((ctx.viewport_id(), "__top_ui")),
|
||||||
plugins.on_end_pass(ui);
|
UiBuilder::new()
|
||||||
});
|
.layer_id(LayerId::background())
|
||||||
|
.max_rect(ctx.available_rect().round_ui()),
|
||||||
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
plugins.on_begin_pass(&mut top_ui);
|
||||||
|
run_ui(&mut top_ui);
|
||||||
|
plugins.on_end_pass(&mut top_ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inform ctx about what we actually used, so we can shrink the native window to fit.
|
||||||
|
// TODO(emilk): make better use of this somehow
|
||||||
|
ctx.pass_state_mut(|state| state.allocate_central_panel(top_ui.min_rect()));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1214,7 +1226,12 @@ impl Context {
|
|||||||
///
|
///
|
||||||
/// `allow_focus` should usually be true, unless you call this function multiple times with the
|
/// `allow_focus` should usually be true, unless you call this function multiple times with the
|
||||||
/// same widget, then `allow_focus` should only be true once (like in [`Ui::new`] (true) and [`Ui::remember_min_rect`] (false)).
|
/// same widget, then `allow_focus` should only be true once (like in [`Ui::new`] (true) and [`Ui::remember_min_rect`] (false)).
|
||||||
pub(crate) fn create_widget(&self, w: WidgetRect, allow_focus: bool) -> Response {
|
pub(crate) fn create_widget(
|
||||||
|
&self,
|
||||||
|
w: WidgetRect,
|
||||||
|
allow_focus: bool,
|
||||||
|
options: crate::InteractOptions,
|
||||||
|
) -> Response {
|
||||||
let interested_in_focus = w.enabled
|
let interested_in_focus = w.enabled
|
||||||
&& w.sense.is_focusable()
|
&& w.sense.is_focusable()
|
||||||
&& self.memory(|mem| mem.allows_interaction(w.layer_id));
|
&& self.memory(|mem| mem.allows_interaction(w.layer_id));
|
||||||
@@ -1226,7 +1243,7 @@ impl Context {
|
|||||||
// We add all widgets here, even non-interactive ones,
|
// We add all widgets here, even non-interactive ones,
|
||||||
// because we need this list not only for checking for blocking widgets,
|
// because we need this list not only for checking for blocking widgets,
|
||||||
// but also to know when we have reached the widget we are checking for cover.
|
// but also to know when we have reached the widget we are checking for cover.
|
||||||
viewport.this_pass.widgets.insert(w.layer_id, w);
|
viewport.this_pass.widgets.insert(w.layer_id, w, options);
|
||||||
|
|
||||||
if allow_focus && interested_in_focus {
|
if allow_focus && interested_in_focus {
|
||||||
ctx.memory.interested_in_focus(w.id, w.layer_id);
|
ctx.memory.interested_in_focus(w.id, w.layer_id);
|
||||||
@@ -1242,7 +1259,7 @@ impl Context {
|
|||||||
self.check_for_id_clash(w.id, w.rect, "widget");
|
self.check_for_id_clash(w.id, w.rect, "widget");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::let_and_return, clippy::allow_attributes)]
|
#[allow(clippy::allow_attributes, clippy::let_and_return)]
|
||||||
let res = self.get_response(w);
|
let res = self.get_response(w);
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
@@ -1947,7 +1964,7 @@ impl Context {
|
|||||||
pub fn add_plugin(&self, plugin: impl plugin::Plugin + 'static) {
|
pub fn add_plugin(&self, plugin: impl plugin::Plugin + 'static) {
|
||||||
let handle = plugin::PluginHandle::new(plugin);
|
let handle = plugin::PluginHandle::new(plugin);
|
||||||
|
|
||||||
let added = self.write(|ctx| ctx.plugins.add(handle.clone()));
|
let added = self.write(|ctx| ctx.plugins.add(Arc::clone(&handle)));
|
||||||
|
|
||||||
if added {
|
if added {
|
||||||
handle.lock().dyn_plugin_mut().setup(self);
|
handle.lock().dyn_plugin_mut().setup(self);
|
||||||
@@ -2073,13 +2090,13 @@ impl Context {
|
|||||||
|
|
||||||
/// The currently active [`Style`] used by all subsequent popups, menus, etc.
|
/// The currently active [`Style`] used by all subsequent popups, menus, etc.
|
||||||
pub fn global_style(&self) -> Arc<Style> {
|
pub fn global_style(&self) -> Arc<Style> {
|
||||||
self.options(|opt| opt.style().clone())
|
self.options(|opt| Arc::clone(opt.style()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The currently active [`Style`] used by all subsequent popups, menus, etc.
|
/// The currently active [`Style`] used by all subsequent popups, menus, etc.
|
||||||
#[deprecated = "Renamed to `global_style` to avoid confusion with `ui.style()`"]
|
#[deprecated = "Renamed to `global_style` to avoid confusion with `ui.style()`"]
|
||||||
pub fn style(&self) -> Arc<Style> {
|
pub fn style(&self) -> Arc<Style> {
|
||||||
self.options(|opt| opt.style().clone())
|
self.options(|opt| Arc::clone(opt.style()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutate the currently active [`Style`] used by all subsequent popups, menus, etc.
|
/// Mutate the currently active [`Style`] used by all subsequent popups, menus, etc.
|
||||||
@@ -2153,8 +2170,8 @@ impl Context {
|
|||||||
/// The [`Style`] used by all subsequent popups, menus, etc.
|
/// The [`Style`] used by all subsequent popups, menus, etc.
|
||||||
pub fn style_of(&self, theme: Theme) -> Arc<Style> {
|
pub fn style_of(&self, theme: Theme) -> Arc<Style> {
|
||||||
self.options(|opt| match theme {
|
self.options(|opt| match theme {
|
||||||
Theme::Dark => opt.dark_style.clone(),
|
Theme::Dark => Arc::clone(&opt.dark_style),
|
||||||
Theme::Light => opt.light_style.clone(),
|
Theme::Light => Arc::clone(&opt.light_style),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2348,7 +2365,7 @@ impl Context {
|
|||||||
///
|
///
|
||||||
/// You can show stats about the allocated textures using [`Self::texture_ui`].
|
/// You can show stats about the allocated textures using [`Self::texture_ui`].
|
||||||
pub fn tex_manager(&self) -> Arc<RwLock<epaint::textures::TextureManager>> {
|
pub fn tex_manager(&self) -> Arc<RwLock<epaint::textures::TextureManager>> {
|
||||||
self.read(|ctx| ctx.tex_manager.0.clone())
|
self.read(|ctx| Arc::clone(&ctx.tex_manager.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
@@ -2799,17 +2816,12 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// How much space is still available after panels have been added.
|
/// How much space is still available after panels have been added.
|
||||||
pub fn globally_available_rect(&self) -> Rect {
|
#[deprecated = "Use content_rect (or viewport_rect) instead"]
|
||||||
|
pub fn available_rect(&self) -> Rect {
|
||||||
self.pass_state(|s| s.available_rect()).round_ui()
|
self.pass_state(|s| s.available_rect()).round_ui()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How much space is still available after panels have been added.
|
/// How much space is used by windows and the top-level [`Ui`].
|
||||||
#[deprecated = "Renamed to globally_available_rect"]
|
|
||||||
pub fn available_rect(&self) -> Rect {
|
|
||||||
self.globally_available_rect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// How much space is used by panels and windows.
|
|
||||||
pub fn globally_used_rect(&self) -> Rect {
|
pub fn globally_used_rect(&self) -> Rect {
|
||||||
self.write(|ctx| {
|
self.write(|ctx| {
|
||||||
let mut used = ctx.viewport().this_pass.used_by_panels;
|
let mut used = ctx.viewport().this_pass.used_by_panels;
|
||||||
@@ -2820,23 +2832,16 @@ impl Context {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How much space is used by panels and windows.
|
/// How much space is used by windows and the top-level [`Ui`].
|
||||||
#[deprecated = "Renamed to globally_used_rect"]
|
#[deprecated = "Renamed to globally_used_rect"]
|
||||||
pub fn used_rect(&self) -> Rect {
|
pub fn used_rect(&self) -> Rect {
|
||||||
self.globally_used_rect()
|
self.globally_used_rect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How much space is used by panels and windows.
|
/// How much space is used by windows and the top-level [`Ui`].
|
||||||
///
|
///
|
||||||
/// You can shrink your egui area to this size and still fit all egui components.
|
/// You can shrink your egui area to this size and still fit all egui components.
|
||||||
pub fn globally_used_size(&self) -> Vec2 {
|
#[deprecated = "Use globally_used_rect instead"]
|
||||||
(self.globally_used_rect().max - Pos2::ZERO).round_ui()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// How much space is used by panels and windows.
|
|
||||||
///
|
|
||||||
/// You can shrink your egui area to this size and still fit all egui components.
|
|
||||||
#[deprecated = "Renamed to globally_used_size"]
|
|
||||||
pub fn used_size(&self) -> Vec2 {
|
pub fn used_size(&self) -> Vec2 {
|
||||||
(self.globally_used_rect().max - Pos2::ZERO).round_ui()
|
(self.globally_used_rect().max - Pos2::ZERO).round_ui()
|
||||||
}
|
}
|
||||||
@@ -3628,7 +3633,7 @@ impl Context {
|
|||||||
/// If AccessKit support is active for the current frame, get or create
|
/// If AccessKit support is active for the current frame, get or create
|
||||||
/// a node builder with the specified ID and return a mutable reference to it.
|
/// a node builder with the specified ID and return a mutable reference to it.
|
||||||
/// For newly created nodes, the parent is the parent [`Ui`]s ID.
|
/// For newly created nodes, the parent is the parent [`Ui`]s ID.
|
||||||
/// And an [`Ui`]s parent can be set with [`crate::UiBuilder::accessibility_parent`].
|
/// And an [`Ui`]s parent can be set with [`UiBuilder::accessibility_parent`].
|
||||||
///
|
///
|
||||||
/// The `Context` lock is held while the given closure is called!
|
/// The `Context` lock is held while the given closure is called!
|
||||||
///
|
///
|
||||||
@@ -3639,14 +3644,7 @@ impl Context {
|
|||||||
id: Id,
|
id: Id,
|
||||||
writer: impl FnOnce(&mut accesskit::Node) -> R,
|
writer: impl FnOnce(&mut accesskit::Node) -> R,
|
||||||
) -> Option<R> {
|
) -> Option<R> {
|
||||||
self.write(|ctx| {
|
self.write(|ctx| ctx.accesskit_node_builder(id).map(writer))
|
||||||
ctx.viewport()
|
|
||||||
.this_pass
|
|
||||||
.accesskit_state
|
|
||||||
.is_some()
|
|
||||||
.then(|| ctx.accesskit_node_builder(id))
|
|
||||||
.map(writer)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn register_accesskit_parent(&self, id: Id, parent_id: Id) {
|
pub(crate) fn register_accesskit_parent(&self, id: Id, parent_id: Id) {
|
||||||
@@ -3887,7 +3885,7 @@ impl Context {
|
|||||||
|
|
||||||
/// The loaders of bytes, images, and textures.
|
/// The loaders of bytes, images, and textures.
|
||||||
pub fn loaders(&self) -> Arc<Loaders> {
|
pub fn loaders(&self) -> Arc<Loaders> {
|
||||||
self.read(|this| this.loaders.clone())
|
self.read(|this| Arc::clone(&this.loaders))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if any image is currently being loaded.
|
/// Returns `true` if any image is currently being loaded.
|
||||||
|
|||||||
@@ -679,6 +679,8 @@ impl WidgetInfo {
|
|||||||
WidgetType::Panel => "panel",
|
WidgetType::Panel => "panel",
|
||||||
WidgetType::ProgressIndicator => "progress indicator",
|
WidgetType::ProgressIndicator => "progress indicator",
|
||||||
WidgetType::Window => "window",
|
WidgetType::Window => "window",
|
||||||
|
WidgetType::ScrollBar => "scroll bar",
|
||||||
|
WidgetType::ResizeHandle => "resize handle",
|
||||||
WidgetType::Label | WidgetType::Other => "",
|
WidgetType::Label | WidgetType::Other => "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -97,11 +97,7 @@ impl DragAndDrop {
|
|||||||
where
|
where
|
||||||
Payload: Any + Send + Sync,
|
Payload: Any + Send + Sync,
|
||||||
{
|
{
|
||||||
ctx.plugin::<Self>()
|
Arc::clone(ctx.plugin::<Self>().lock().payload.as_ref()?)
|
||||||
.lock()
|
|
||||||
.payload
|
|
||||||
.as_ref()?
|
|
||||||
.clone()
|
|
||||||
.downcast()
|
.downcast()
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use emath::GuiRounding as _;
|
use emath::GuiRounding as _;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -102,7 +104,7 @@ impl GridLayout {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
ctx: ui.ctx().clone(),
|
ctx: ui.ctx().clone(),
|
||||||
style: ui.style().clone(),
|
style: Arc::clone(ui.style()),
|
||||||
id,
|
id,
|
||||||
is_first_frame,
|
is_first_frame,
|
||||||
prev_state,
|
prev_state,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use ahash::HashMap;
|
|||||||
|
|
||||||
use emath::TSTransform;
|
use emath::TSTransform;
|
||||||
|
|
||||||
use crate::{LayerId, Pos2, Rect, Sense, WidgetRect, WidgetRects, ahash, emath, id::IdSet};
|
use crate::{LayerId, Pos2, Sense, WidgetRect, WidgetRects, ahash, emath, id::IdSet};
|
||||||
|
|
||||||
/// Result of a hit-test against [`WidgetRects`].
|
/// Result of a hit-test against [`WidgetRects`].
|
||||||
///
|
///
|
||||||
@@ -201,7 +201,7 @@ fn contains_circle(interact_rect: emath::Rect, pos: Pos2, radius: f32) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn hit_test_on_close(close: &[WidgetRect], pos: Pos2) -> WidgetHits {
|
fn hit_test_on_close(close: &[WidgetRect], pos: Pos2) -> WidgetHits {
|
||||||
#![allow(clippy::collapsible_else_if)]
|
#![expect(clippy::collapsible_else_if)]
|
||||||
|
|
||||||
// First find the best direct hits:
|
// First find the best direct hits:
|
||||||
let hit_click = find_closest_within(
|
let hit_click = find_closest_within(
|
||||||
@@ -361,7 +361,10 @@ fn hit_test_on_close(close: &[WidgetRect], pos: Pos2) -> WidgetHits {
|
|||||||
|
|
||||||
(Some(hit_click), Some(hit_drag)) => {
|
(Some(hit_click), Some(hit_drag)) => {
|
||||||
// We have a perfect hit on both click and drag. Which is the topmost?
|
// We have a perfect hit on both click and drag. Which is the topmost?
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
let click_idx = close.iter().position(|w| *w == hit_click).unwrap();
|
let click_idx = close.iter().position(|w| *w == hit_click).unwrap();
|
||||||
|
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
let drag_idx = close.iter().position(|w| *w == hit_drag).unwrap();
|
let drag_idx = close.iter().position(|w| *w == hit_drag).unwrap();
|
||||||
|
|
||||||
let click_is_on_top_of_drag = drag_idx < click_idx;
|
let click_is_on_top_of_drag = drag_idx < click_idx;
|
||||||
@@ -424,16 +427,6 @@ fn find_closest_within(
|
|||||||
|
|
||||||
let dist_sq = widget.interact_rect.distance_sq_to_pos(pos);
|
let dist_sq = widget.interact_rect.distance_sq_to_pos(pos);
|
||||||
|
|
||||||
if let Some(closest) = closest
|
|
||||||
&& dist_sq == closest_dist_sq
|
|
||||||
{
|
|
||||||
// It's a tie! Pick the thin candidate over the thick one.
|
|
||||||
// This makes it easier to hit a thin resize-handle, for instance:
|
|
||||||
if should_prioritize_hits_on_back(closest.interact_rect, widget.interact_rect) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case of a tie, take the last one = the one on top.
|
// In case of a tie, take the last one = the one on top.
|
||||||
if dist_sq <= closest_dist_sq {
|
if dist_sq <= closest_dist_sq {
|
||||||
closest_dist_sq = dist_sq;
|
closest_dist_sq = dist_sq;
|
||||||
@@ -444,27 +437,6 @@ fn find_closest_within(
|
|||||||
closest
|
closest
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Should we prioritize hits on `back` over those on `front`?
|
|
||||||
///
|
|
||||||
/// `back` should be behind the `front` widget.
|
|
||||||
///
|
|
||||||
/// Returns true if `back` is a small hit-target and `front` is not.
|
|
||||||
fn should_prioritize_hits_on_back(back: Rect, front: Rect) -> bool {
|
|
||||||
if front.contains_rect(back) {
|
|
||||||
return false; // back widget is fully occluded; no way to hit it
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reduce each rect to its width or height, whichever is smaller:
|
|
||||||
let back = back.width().min(back.height());
|
|
||||||
let front = front.width().min(front.height());
|
|
||||||
|
|
||||||
// These are hard-coded heuristics that could surely be improved.
|
|
||||||
let back_is_much_thinner = back <= 0.5 * front;
|
|
||||||
let back_is_thin = back <= 16.0;
|
|
||||||
|
|
||||||
back_is_much_thinner && back_is_thin
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#![expect(clippy::print_stdout)]
|
#![expect(clippy::print_stdout)]
|
||||||
|
|||||||
@@ -631,8 +631,10 @@ impl InputState {
|
|||||||
/// A positive Y-value indicates the content is being moved down, as when swiping down on a touch-screen or track-pad with natural scrolling.
|
/// A positive Y-value indicates the content is being moved down, as when swiping down on a touch-screen or track-pad with natural scrolling.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn translation_delta(&self) -> Vec2 {
|
pub fn translation_delta(&self) -> Vec2 {
|
||||||
self.multi_touch()
|
self.multi_touch().map_or_else(
|
||||||
.map_or(self.smooth_scroll_delta(), |touch| touch.translation_delta)
|
|| self.smooth_scroll_delta(),
|
||||||
|
|touch| touch.translation_delta,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// True if there is an active scroll action that might scroll more when using [`Self::smooth_scroll_delta`].
|
/// True if there is an active scroll action that might scroll more when using [`Self::smooth_scroll_delta`].
|
||||||
@@ -1549,11 +1551,9 @@ impl InputState {
|
|||||||
options: _,
|
options: _,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
ui.style_mut()
|
if let Some(style) = ui.style_mut().text_styles.get_mut(&crate::TextStyle::Body) {
|
||||||
.text_styles
|
style.family = crate::FontFamily::Monospace;
|
||||||
.get_mut(&crate::TextStyle::Body)
|
}
|
||||||
.unwrap()
|
|
||||||
.family = crate::FontFamily::Monospace;
|
|
||||||
|
|
||||||
ui.collapsing("Raw Input", |ui| raw.ui(ui));
|
ui.collapsing("Raw Input", |ui| raw.ui(ui));
|
||||||
|
|
||||||
|
|||||||
@@ -288,6 +288,7 @@ impl TouchState {
|
|||||||
// touch individually, and then calculate the average of all individual changes in
|
// touch individually, and then calculate the average of all individual changes in
|
||||||
// direction. But this approach cannot be implemented locally in this method, making
|
// direction. But this approach cannot be implemented locally in this method, making
|
||||||
// everything a bit more complicated.
|
// everything a bit more complicated.
|
||||||
|
#[expect(clippy::unwrap_used)] // guarded against already
|
||||||
let first_touch = self.active_touches.values().next().unwrap();
|
let first_touch = self.active_touches.values().next().unwrap();
|
||||||
state.heading = (state.avg_pos - first_touch.pos).angle();
|
state.heading = (state.avg_pos - first_touch.pos).angle();
|
||||||
|
|
||||||
@@ -323,13 +324,14 @@ enum PinchType {
|
|||||||
|
|
||||||
impl PinchType {
|
impl PinchType {
|
||||||
fn classify(touches: &BTreeMap<TouchId, ActiveTouch>) -> Self {
|
fn classify(touches: &BTreeMap<TouchId, ActiveTouch>) -> Self {
|
||||||
|
#![expect(clippy::unwrap_used)]
|
||||||
|
|
||||||
// For non-proportional 2d zooming:
|
// For non-proportional 2d zooming:
|
||||||
// If the user is pinching with two fingers that have roughly the same Y coord,
|
// If the user is pinching with two fingers that have roughly the same Y coord,
|
||||||
// then the Y zoom is unstable and should be 1.
|
// then the Y zoom is unstable and should be 1.
|
||||||
// Similarly, if the fingers are directly above/below each other,
|
// Similarly, if the fingers are directly above/below each other,
|
||||||
// we should only zoom on the Y axis.
|
// we should only zoom on the Y axis.
|
||||||
// If the fingers are roughly on a diagonal, we revert to the proportional zooming.
|
// If the fingers are roughly on a diagonal, we revert to the proportional zooming.
|
||||||
|
|
||||||
if touches.len() == 2 {
|
if touches.len() == 2 {
|
||||||
let mut touches = touches.values();
|
let mut touches = touches.values();
|
||||||
let t0 = touches.next().unwrap().pos;
|
let t0 = touches.next().unwrap().pos;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//! Try the live web demo: <https://www.egui.rs/#demo>. Read more about egui at <https://github.com/emilk/egui>.
|
//! Try the live web demo: <https://www.egui.rs/#demo>. Read more about egui at <https://github.com/emilk/egui>.
|
||||||
//!
|
//!
|
||||||
//! `egui` is in heavy development, with each new version having breaking changes.
|
//! `egui` is in heavy development, with each new version having breaking changes.
|
||||||
//! You need to have rust 1.88.0 or later to use `egui`.
|
//! You need to have rust 1.92.0 or later to use `egui`.
|
||||||
//!
|
//!
|
||||||
//! To quickly get started with egui, you can take a look at [`eframe_template`](https://github.com/emilk/eframe_template)
|
//! To quickly get started with egui, you can take a look at [`eframe_template`](https://github.com/emilk/eframe_template)
|
||||||
//! which uses [`eframe`](https://docs.rs/eframe).
|
//! which uses [`eframe`](https://docs.rs/eframe).
|
||||||
@@ -385,8 +385,8 @@
|
|||||||
//! egui apps can run significantly (~20%) faster by using a custom allocator, like [mimalloc](https://crates.io/crates/mimalloc) or [talc](https://crates.io/crates/talc).
|
//! egui apps can run significantly (~20%) faster by using a custom allocator, like [mimalloc](https://crates.io/crates/mimalloc) or [talc](https://crates.io/crates/talc).
|
||||||
//!
|
//!
|
||||||
|
|
||||||
#![allow(clippy::float_cmp)]
|
#![expect(clippy::float_cmp)]
|
||||||
#![allow(clippy::manual_range_contains)]
|
#![expect(clippy::manual_range_contains)]
|
||||||
|
|
||||||
mod animation_manager;
|
mod animation_manager;
|
||||||
mod atomics;
|
mod atomics;
|
||||||
@@ -494,7 +494,7 @@ pub use self::{
|
|||||||
ui_builder::UiBuilder,
|
ui_builder::UiBuilder,
|
||||||
ui_stack::*,
|
ui_stack::*,
|
||||||
viewport::*,
|
viewport::*,
|
||||||
widget_rect::{WidgetRect, WidgetRects},
|
widget_rect::{InteractOptions, WidgetRect, WidgetRects},
|
||||||
widget_text::{RichText, WidgetText},
|
widget_text::{RichText, WidgetText},
|
||||||
widgets::*,
|
widgets::*,
|
||||||
};
|
};
|
||||||
@@ -663,6 +663,10 @@ pub enum WidgetType {
|
|||||||
|
|
||||||
Window,
|
Window,
|
||||||
|
|
||||||
|
ResizeHandle,
|
||||||
|
|
||||||
|
ScrollBar,
|
||||||
|
|
||||||
/// If you cannot fit any of the above slots.
|
/// If you cannot fit any of the above slots.
|
||||||
///
|
///
|
||||||
/// If this is something you think should be added, file an issue.
|
/// If this is something you think should be added, file an issue.
|
||||||
|
|||||||
@@ -609,7 +609,7 @@ impl Default for Loaders {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let include = Arc::new(DefaultBytesLoader::default());
|
let include = Arc::new(DefaultBytesLoader::default());
|
||||||
Self {
|
Self {
|
||||||
bytes: Mutex::new(vec![include.clone()]),
|
bytes: Mutex::new(vec![Arc::clone(&include) as _]),
|
||||||
image: Mutex::new(Vec::new()),
|
image: Mutex::new(Vec::new()),
|
||||||
// By default we only include `DefaultTextureLoader`.
|
// By default we only include `DefaultTextureLoader`.
|
||||||
texture: Mutex::new(vec![Arc::new(DefaultTextureLoader::default())]),
|
texture: Mutex::new(vec![Arc::new(DefaultTextureLoader::default())]),
|
||||||
|
|||||||
@@ -306,6 +306,8 @@ impl Default for Options {
|
|||||||
zoom_with_keyboard: true,
|
zoom_with_keyboard: true,
|
||||||
tessellation_options: Default::default(),
|
tessellation_options: Default::default(),
|
||||||
repaint_on_widget_change: false,
|
repaint_on_widget_change: false,
|
||||||
|
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
max_passes: NonZeroUsize::new(2).unwrap(),
|
max_passes: NonZeroUsize::new(2).unwrap(),
|
||||||
screen_reader: false,
|
screen_reader: false,
|
||||||
warn_on_id_clash: cfg!(debug_assertions),
|
warn_on_id_clash: cfg!(debug_assertions),
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ impl Theme {
|
|||||||
/// This is not the best design as it doesn't allow switching back to "follow system".
|
/// This is not the best design as it doesn't allow switching back to "follow system".
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn small_toggle_button(self, ui: &mut crate::Ui) -> Option<Self> {
|
pub(crate) fn small_toggle_button(self, ui: &mut crate::Ui) -> Option<Self> {
|
||||||
#![allow(clippy::collapsible_else_if)]
|
#![expect(clippy::collapsible_else_if)]
|
||||||
if self == Self::Dark {
|
if self == Self::Dark {
|
||||||
if ui
|
if ui
|
||||||
.add(Button::new("☀").frame(false))
|
.add(Button::new("☀").frame(false))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![allow(deprecated)]
|
#![expect(deprecated)]
|
||||||
//! Deprecated menu API - Use [`crate::containers::menu`] instead.
|
//! Deprecated menu API - Use [`crate::containers::menu`] instead.
|
||||||
//!
|
//!
|
||||||
//! Usage:
|
//! Usage:
|
||||||
@@ -198,7 +198,7 @@ fn menu_popup<'c, R>(
|
|||||||
|
|
||||||
Frame::menu(ui.style())
|
Frame::menu(ui.style())
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.set_menu_state(Some(menu_state_arc.clone()));
|
ui.set_menu_state(Some(Arc::clone(menu_state_arc)));
|
||||||
ui.with_layout(Layout::top_down_justified(Align::LEFT), add_contents)
|
ui.with_layout(Layout::top_down_justified(Align::LEFT), add_contents)
|
||||||
.inner
|
.inner
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ impl Plugins {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.plugins.insert(type_id, handle.clone());
|
self.plugins.insert(type_id, Arc::clone(&handle));
|
||||||
self.plugins_ordered.0.push(handle);
|
self.plugins_ordered.0.push(handle);
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use crate::{
|
|||||||
/// It also lets you easily show a tooltip on hover.
|
/// It also lets you easily show a tooltip on hover.
|
||||||
///
|
///
|
||||||
/// Whenever something gets added to a [`Ui`], a [`Response`] object is returned.
|
/// Whenever something gets added to a [`Ui`], a [`Response`] object is returned.
|
||||||
/// [`ui.add`] returns a [`Response`], as does [`ui.button`], and all similar shortcuts.
|
/// [`Ui::add`] returns a [`Response`], as does [`Ui::button`], and all similar shortcuts.
|
||||||
///
|
///
|
||||||
/// ⚠️ The `Response` contains a clone of [`Context`], and many methods lock the `Context`.
|
/// ⚠️ The `Response` contains a clone of [`Context`], and many methods lock the `Context`.
|
||||||
/// It can therefore be a deadlock to use `Context` from within a context-locking closures,
|
/// It can therefore be a deadlock to use `Context` from within a context-locking closures,
|
||||||
@@ -738,6 +738,7 @@ impl Response {
|
|||||||
enabled: self.enabled(),
|
enabled: self.enabled(),
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
Default::default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,6 +872,10 @@ impl Response {
|
|||||||
WidgetType::Panel => Role::Pane,
|
WidgetType::Panel => Role::Pane,
|
||||||
WidgetType::ProgressIndicator => Role::ProgressIndicator,
|
WidgetType::ProgressIndicator => Role::ProgressIndicator,
|
||||||
WidgetType::Window => Role::Window,
|
WidgetType::Window => Role::Window,
|
||||||
|
|
||||||
|
WidgetType::ResizeHandle => Role::Splitter,
|
||||||
|
WidgetType::ScrollBar => Role::ScrollBar,
|
||||||
|
|
||||||
WidgetType::Other => Role::Unknown,
|
WidgetType::Other => Role::Unknown,
|
||||||
});
|
});
|
||||||
if !info.enabled {
|
if !info.enabled {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
//! egui theme (spacing, colors, etc).
|
//! egui theme (spacing, colors, etc).
|
||||||
|
|
||||||
#![allow(clippy::if_same_then_else)]
|
|
||||||
|
|
||||||
use emath::Align;
|
use emath::Align;
|
||||||
use epaint::{AlphaFromCoverage, CornerRadius, Shadow, Stroke, TextOptions, text::FontTweak};
|
use epaint::{AlphaFromCoverage, CornerRadius, Shadow, Stroke, TextOptions, text::FontTweak};
|
||||||
use std::{collections::BTreeMap, ops::RangeInclusive, sync::Arc};
|
use std::{collections::BTreeMap, ops::RangeInclusive, sync::Arc};
|
||||||
@@ -1236,6 +1234,11 @@ pub struct WidgetVisuals {
|
|||||||
pub fg_stroke: Stroke,
|
pub fg_stroke: Stroke,
|
||||||
|
|
||||||
/// Make the frame this much larger.
|
/// Make the frame this much larger.
|
||||||
|
///
|
||||||
|
/// The problem with "expanding" widgets is that they now want to paint outside their own bounds,
|
||||||
|
/// which then requires all parent UIs to have proper margins.
|
||||||
|
///
|
||||||
|
/// It also means hovered things are no longer properly aligned with every other widget.
|
||||||
pub expansion: f32,
|
pub expansion: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1394,7 +1397,7 @@ impl Default for Interaction {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
interact_radius: 5.0,
|
interact_radius: 5.0,
|
||||||
resize_grab_radius_side: 5.0,
|
resize_grab_radius_side: 3.0,
|
||||||
resize_grab_radius_corner: 10.0,
|
resize_grab_radius_corner: 10.0,
|
||||||
show_tooltips_only_when_still: true,
|
show_tooltips_only_when_still: true,
|
||||||
tooltip_delay: 0.5,
|
tooltip_delay: 0.5,
|
||||||
@@ -1570,7 +1573,7 @@ impl Widgets {
|
|||||||
bg_stroke: Stroke::new(1.0, Color32::from_gray(150)), // e.g. hover over window edge or button
|
bg_stroke: Stroke::new(1.0, Color32::from_gray(150)), // e.g. hover over window edge or button
|
||||||
fg_stroke: Stroke::new(1.5, Color32::from_gray(240)),
|
fg_stroke: Stroke::new(1.5, Color32::from_gray(240)),
|
||||||
corner_radius: CornerRadius::same(3),
|
corner_radius: CornerRadius::same(3),
|
||||||
expansion: 1.0,
|
expansion: 0.0,
|
||||||
},
|
},
|
||||||
active: WidgetVisuals {
|
active: WidgetVisuals {
|
||||||
weak_bg_fill: Color32::from_gray(55),
|
weak_bg_fill: Color32::from_gray(55),
|
||||||
@@ -1578,7 +1581,7 @@ impl Widgets {
|
|||||||
bg_stroke: Stroke::new(1.0, Color32::WHITE),
|
bg_stroke: Stroke::new(1.0, Color32::WHITE),
|
||||||
fg_stroke: Stroke::new(2.0, Color32::WHITE),
|
fg_stroke: Stroke::new(2.0, Color32::WHITE),
|
||||||
corner_radius: CornerRadius::same(2),
|
corner_radius: CornerRadius::same(2),
|
||||||
expansion: 1.0,
|
expansion: 0.0,
|
||||||
},
|
},
|
||||||
open: WidgetVisuals {
|
open: WidgetVisuals {
|
||||||
weak_bg_fill: Color32::from_gray(45),
|
weak_bg_fill: Color32::from_gray(45),
|
||||||
@@ -1615,7 +1618,7 @@ impl Widgets {
|
|||||||
bg_stroke: Stroke::new(1.0, Color32::from_gray(105)), // e.g. hover over window edge or button
|
bg_stroke: Stroke::new(1.0, Color32::from_gray(105)), // e.g. hover over window edge or button
|
||||||
fg_stroke: Stroke::new(1.5, Color32::BLACK),
|
fg_stroke: Stroke::new(1.5, Color32::BLACK),
|
||||||
corner_radius: CornerRadius::same(3),
|
corner_radius: CornerRadius::same(3),
|
||||||
expansion: 1.0,
|
expansion: 0.0,
|
||||||
},
|
},
|
||||||
active: WidgetVisuals {
|
active: WidgetVisuals {
|
||||||
weak_bg_fill: Color32::from_gray(165),
|
weak_bg_fill: Color32::from_gray(165),
|
||||||
@@ -1623,7 +1626,7 @@ impl Widgets {
|
|||||||
bg_stroke: Stroke::new(1.0, Color32::BLACK),
|
bg_stroke: Stroke::new(1.0, Color32::BLACK),
|
||||||
fg_stroke: Stroke::new(2.0, Color32::BLACK),
|
fg_stroke: Stroke::new(2.0, Color32::BLACK),
|
||||||
corner_radius: CornerRadius::same(2),
|
corner_radius: CornerRadius::same(2),
|
||||||
expansion: 1.0,
|
expansion: 0.0,
|
||||||
},
|
},
|
||||||
open: WidgetVisuals {
|
open: WidgetVisuals {
|
||||||
weak_bg_fill: Color32::from_gray(220),
|
weak_bg_fill: Color32::from_gray(220),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(missing_docs)] // Let's keep `Ui` well-documented.
|
#![warn(missing_docs)] // Let's keep `Ui` well-documented.
|
||||||
#![allow(clippy::use_self)]
|
#![expect(clippy::use_self)]
|
||||||
|
|
||||||
use std::{any::Any, hash::Hash, ops::Deref, sync::Arc};
|
use std::{any::Any, hash::Hash, ops::Deref, sync::Arc};
|
||||||
|
|
||||||
@@ -180,6 +180,7 @@ impl Ui {
|
|||||||
enabled: ui.enabled,
|
enabled: ui.enabled,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
Default::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if disabled {
|
if disabled {
|
||||||
@@ -275,7 +276,7 @@ impl Ui {
|
|||||||
painter.set_invisible();
|
painter.set_invisible();
|
||||||
}
|
}
|
||||||
let sizing_pass = self.sizing_pass || sizing_pass;
|
let sizing_pass = self.sizing_pass || sizing_pass;
|
||||||
let style = style.unwrap_or_else(|| self.style.clone());
|
let style = style.unwrap_or_else(|| Arc::clone(&self.style));
|
||||||
let sense = sense.unwrap_or_else(Sense::hover);
|
let sense = sense.unwrap_or_else(Sense::hover);
|
||||||
|
|
||||||
if sizing_pass {
|
if sizing_pass {
|
||||||
@@ -305,7 +306,7 @@ impl Ui {
|
|||||||
id: unique_id,
|
id: unique_id,
|
||||||
layout_direction: layout.main_dir,
|
layout_direction: layout.main_dir,
|
||||||
info: ui_stack_info,
|
info: ui_stack_info,
|
||||||
parent: Some(self.stack.clone()),
|
parent: Some(Arc::clone(&self.stack)),
|
||||||
min_rect: placer.min_rect(),
|
min_rect: placer.min_rect(),
|
||||||
max_rect: placer.max_rect(),
|
max_rect: placer.max_rect(),
|
||||||
};
|
};
|
||||||
@@ -345,6 +346,7 @@ impl Ui {
|
|||||||
enabled: child_ui.enabled,
|
enabled: child_ui.enabled,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
Default::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
child_ui
|
child_ui
|
||||||
@@ -1025,6 +1027,17 @@ impl Ui {
|
|||||||
impl Ui {
|
impl Ui {
|
||||||
/// Check for clicks, drags and/or hover on a specific region of this [`Ui`].
|
/// Check for clicks, drags and/or hover on a specific region of this [`Ui`].
|
||||||
pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> Response {
|
pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> Response {
|
||||||
|
self.interact_opt(rect, id, sense, Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check for clicks, drags and/or hover on a specific region of this [`Ui`].
|
||||||
|
pub fn interact_opt(
|
||||||
|
&self,
|
||||||
|
rect: Rect,
|
||||||
|
id: Id,
|
||||||
|
sense: Sense,
|
||||||
|
options: crate::InteractOptions,
|
||||||
|
) -> Response {
|
||||||
self.ctx().register_accesskit_parent(id, self.unique_id);
|
self.ctx().register_accesskit_parent(id, self.unique_id);
|
||||||
|
|
||||||
self.ctx().create_widget(
|
self.ctx().create_widget(
|
||||||
@@ -1037,6 +1050,7 @@ impl Ui {
|
|||||||
enabled: self.enabled,
|
enabled: self.enabled,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
options,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1105,6 +1119,7 @@ impl Ui {
|
|||||||
enabled: self.enabled,
|
enabled: self.enabled,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
Default::default(),
|
||||||
);
|
);
|
||||||
if self.should_close() {
|
if self.should_close() {
|
||||||
response.set_close();
|
response.set_close();
|
||||||
|
|||||||
@@ -142,7 +142,9 @@ impl Element {
|
|||||||
Self::Value {
|
Self::Value {
|
||||||
value: Box::new(t),
|
value: Box::new(t),
|
||||||
clone_fn: |x| {
|
clone_fn: |x| {
|
||||||
let x = x.downcast_ref::<T>().unwrap(); // This unwrap will never panic, because we always construct this type using this `new` function and because we return &mut reference only with this type `T`, so type cannot change.
|
// This unwrap will never panic, because we always construct this type using this `new` function and because we return &mut reference only with this type `T`, so type cannot change.
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
|
let x = x.downcast_ref::<T>().unwrap();
|
||||||
Box::new(x.clone())
|
Box::new(x.clone())
|
||||||
},
|
},
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
@@ -156,12 +158,16 @@ impl Element {
|
|||||||
Self::Value {
|
Self::Value {
|
||||||
value: Box::new(t),
|
value: Box::new(t),
|
||||||
clone_fn: |x| {
|
clone_fn: |x| {
|
||||||
let x = x.downcast_ref::<T>().unwrap(); // This unwrap will never panic, because we always construct this type using this `new` function and because we return &mut reference only with this type `T`, so type cannot change.
|
// This unwrap will never panic, because we always construct this type using this `new` function and because we return &mut reference only with this type `T`, so type cannot change.
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
|
let x = x.downcast_ref::<T>().unwrap();
|
||||||
Box::new(x.clone())
|
Box::new(x.clone())
|
||||||
},
|
},
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
serialize_fn: Some(|x| {
|
serialize_fn: Some(|x| {
|
||||||
let x = x.downcast_ref::<T>().unwrap(); // This will never panic too, for same reason.
|
// This will never panic too, for same reason.
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
|
let x = x.downcast_ref::<T>().unwrap();
|
||||||
ron::to_string(x).ok()
|
ron::to_string(x).ok()
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@@ -209,7 +215,9 @@ impl Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Value { value, .. } => value.downcast_mut().unwrap(), // This unwrap will never panic because we already converted object to required type
|
// This unwrap will never panic because we already converted object to required type
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
|
Self::Value { value, .. } => value.downcast_mut().unwrap(),
|
||||||
Self::Serialized(_) => unreachable!(),
|
Self::Serialized(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,7 +246,9 @@ impl Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Value { value, .. } => value.downcast_mut().unwrap(), // This unwrap will never panic because we already converted object to required type
|
// This unwrap will never panic because we already converted object to required type
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
|
Self::Value { value, .. } => value.downcast_mut().unwrap(),
|
||||||
Self::Serialized(_) => unreachable!(),
|
Self::Serialized(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -436,10 +446,14 @@ impl IdTypeMap {
|
|||||||
let hash = hash(TypeId::of::<T>(), id);
|
let hash = hash(TypeId::of::<T>(), id);
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
match self.map.entry(hash) {
|
match self.map.entry(hash) {
|
||||||
Entry::Vacant(vacant) => vacant
|
Entry::Vacant(vacant) => {
|
||||||
.insert(Element::new_temp(insert_with()))
|
// this unwrap will never panic, because we insert correct type right now
|
||||||
.get_mut_temp()
|
#[expect(clippy::unwrap_used)]
|
||||||
.unwrap(), // this unwrap will never panic, because we insert correct type right now
|
vacant
|
||||||
|
.insert(Element::new_temp(insert_with()))
|
||||||
|
.get_mut_temp()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
Entry::Occupied(occupied) => {
|
Entry::Occupied(occupied) => {
|
||||||
occupied.into_mut().get_temp_mut_or_insert_with(insert_with)
|
occupied.into_mut().get_temp_mut_or_insert_with(insert_with)
|
||||||
}
|
}
|
||||||
@@ -454,10 +468,14 @@ impl IdTypeMap {
|
|||||||
let hash = hash(TypeId::of::<T>(), id);
|
let hash = hash(TypeId::of::<T>(), id);
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
match self.map.entry(hash) {
|
match self.map.entry(hash) {
|
||||||
Entry::Vacant(vacant) => vacant
|
Entry::Vacant(vacant) => {
|
||||||
.insert(Element::new_persisted(insert_with()))
|
// this unwrap will never panic, because we insert correct type right now
|
||||||
.get_mut_persisted()
|
#[expect(clippy::unwrap_used)]
|
||||||
.unwrap(), // this unwrap will never panic, because we insert correct type right now
|
vacant
|
||||||
|
.insert(Element::new_persisted(insert_with()))
|
||||||
|
.get_mut_persisted()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
Entry::Occupied(occupied) => occupied
|
Entry::Occupied(occupied) => occupied
|
||||||
.into_mut()
|
.into_mut()
|
||||||
.get_persisted_mut_or_insert_with(insert_with),
|
.get_persisted_mut_or_insert_with(insert_with),
|
||||||
@@ -466,7 +484,7 @@ impl IdTypeMap {
|
|||||||
|
|
||||||
/// For tests
|
/// For tests
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
#[allow(unused, clippy::allow_attributes)]
|
#[allow(clippy::allow_attributes, unused)]
|
||||||
fn get_generation<T: SerializableAny>(&self, id: Id) -> Option<usize> {
|
fn get_generation<T: SerializableAny>(&self, id: Id) -> Option<usize> {
|
||||||
let element = self.map.get(&hash(TypeId::of::<T>(), id))?;
|
let element = self.map.get(&hash(TypeId::of::<T>(), id))?;
|
||||||
match element {
|
match element {
|
||||||
@@ -706,7 +724,7 @@ fn test_two_id_two_type() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_two_id_x_two_types() {
|
fn test_two_id_x_two_types() {
|
||||||
#![allow(clippy::approx_constant)]
|
#![expect(clippy::approx_constant)]
|
||||||
|
|
||||||
let a = Id::new("a");
|
let a = Id::new("a");
|
||||||
let b = Id::new("b");
|
let b = Id::new("b");
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ where
|
|||||||
self.flux = None;
|
self.flux = None;
|
||||||
|
|
||||||
if self.undos.back() == Some(current_state) {
|
if self.undos.back() == Some(current_state) {
|
||||||
|
#[expect(clippy::unwrap_used)] // we just checked that undos is not empty
|
||||||
self.redos.push(self.undos.pop_back().unwrap());
|
self.redos.push(self.undos.pop_back().unwrap());
|
||||||
} else {
|
} else {
|
||||||
self.redos.push(current_state.clone());
|
self.redos.push(current_state.clone());
|
||||||
|
|||||||
@@ -787,7 +787,7 @@ impl ViewportBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if is_new {
|
if is_new {
|
||||||
commands.push(ViewportCommand::Icon(Some(new_icon.clone())));
|
commands.push(ViewportCommand::Icon(Some(Arc::clone(&new_icon))));
|
||||||
self.icon = Some(new_icon);
|
self.icon = Some(new_icon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,21 @@ impl WidgetRect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// How to handle multiple calls to [`crate::Response::interact`] and [`crate::Ui::interact_opt`].
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct InteractOptions {
|
||||||
|
/// If we call interact on the same widget multiple times,
|
||||||
|
/// should we move it to the top on subsequent calls?
|
||||||
|
pub move_to_top: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::derivable_impls)] // Nice to be explicit
|
||||||
|
impl Default for InteractOptions {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { move_to_top: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Stores the [`WidgetRect`]s of all widgets generated during a single egui update/frame.
|
/// Stores the [`WidgetRect`]s of all widgets generated during a single egui update/frame.
|
||||||
///
|
///
|
||||||
/// All [`crate::Ui`]s have a [`WidgetRect`]. It is created in [`crate::Ui::new`] with [`Rect::NOTHING`]
|
/// All [`crate::Ui`]s have a [`WidgetRect`]. It is created in [`crate::Ui::new`] with [`Rect::NOTHING`]
|
||||||
@@ -140,13 +155,15 @@ impl WidgetRects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Insert the given widget rect in the given layer.
|
/// Insert the given widget rect in the given layer.
|
||||||
pub fn insert(&mut self, layer_id: LayerId, widget_rect: WidgetRect) {
|
pub fn insert(&mut self, layer_id: LayerId, widget_rect: WidgetRect, options: InteractOptions) {
|
||||||
let Self {
|
let Self {
|
||||||
by_layer,
|
by_layer,
|
||||||
by_id,
|
by_id,
|
||||||
infos: _,
|
infos: _,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
|
let InteractOptions { move_to_top } = options;
|
||||||
|
|
||||||
let layer_widgets = by_layer.entry(layer_id).or_default();
|
let layer_widgets = by_layer.entry(layer_id).or_default();
|
||||||
|
|
||||||
match by_id.entry(widget_rect.id) {
|
match by_id.entry(widget_rect.id) {
|
||||||
@@ -161,14 +178,6 @@ impl WidgetRects {
|
|||||||
// e.g. calling `response.interact(…)` to add more interaction.
|
// e.g. calling `response.interact(…)` to add more interaction.
|
||||||
let (idx_in_layer, existing) = entry.get_mut();
|
let (idx_in_layer, existing) = entry.get_mut();
|
||||||
|
|
||||||
debug_assert!(
|
|
||||||
existing.layer_id == widget_rect.layer_id,
|
|
||||||
"Widget {:?} changed layer_id during the frame from {:?} to {:?}",
|
|
||||||
widget_rect.id,
|
|
||||||
existing.layer_id,
|
|
||||||
widget_rect.layer_id
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update it:
|
// Update it:
|
||||||
existing.rect = widget_rect.rect; // last wins
|
existing.rect = widget_rect.rect; // last wins
|
||||||
existing.interact_rect = widget_rect.interact_rect; // last wins
|
existing.interact_rect = widget_rect.interact_rect; // last wins
|
||||||
@@ -176,7 +185,18 @@ impl WidgetRects {
|
|||||||
existing.enabled |= widget_rect.enabled;
|
existing.enabled |= widget_rect.enabled;
|
||||||
|
|
||||||
if existing.layer_id == widget_rect.layer_id {
|
if existing.layer_id == widget_rect.layer_id {
|
||||||
layer_widgets[*idx_in_layer] = *existing;
|
if move_to_top {
|
||||||
|
layer_widgets.remove(*idx_in_layer);
|
||||||
|
*idx_in_layer = layer_widgets.len();
|
||||||
|
layer_widgets.push(*existing);
|
||||||
|
} else {
|
||||||
|
layer_widgets[*idx_in_layer] = *existing;
|
||||||
|
}
|
||||||
|
} else if cfg!(debug_assertions) {
|
||||||
|
panic!(
|
||||||
|
"DEBUG ASSERT: Widget {:?} changed layer_id during the frame from {:?} to {:?}",
|
||||||
|
widget_rect.id, existing.layer_id, widget_rect.layer_id
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -709,7 +709,7 @@ impl WidgetText {
|
|||||||
default_valign,
|
default_valign,
|
||||||
)),
|
)),
|
||||||
Self::LayoutJob(job) => job,
|
Self::LayoutJob(job) => job,
|
||||||
Self::Galley(galley) => galley.job.clone(),
|
Self::Galley(galley) => Arc::clone(&galley.job),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ fn color_button(ui: &mut Ui, color: Color32, open: bool) -> Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn color_slider_1d(ui: &mut Ui, value: &mut f32, color_at: impl Fn(f32) -> Color32) -> Response {
|
fn color_slider_1d(ui: &mut Ui, value: &mut f32, color_at: impl Fn(f32) -> Color32) -> Response {
|
||||||
#![allow(clippy::identity_op)]
|
#![expect(clippy::identity_op)]
|
||||||
|
|
||||||
let desired_size = vec2(ui.spacing().slider_width, ui.spacing().interact_size.y);
|
let desired_size = vec2(ui.spacing().slider_width, ui.spacing().interact_size.y);
|
||||||
let (rect, response) = ui.allocate_at_least(desired_size, Sense::click_and_drag());
|
let (rect, response) = ui.allocate_at_least(desired_size, Sense::click_and_drag());
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![allow(clippy::needless_pass_by_value)] // False positives with `impl ToString`
|
#![expect(clippy::needless_pass_by_value)] // False positives with `impl ToString`
|
||||||
|
|
||||||
use std::{cmp::Ordering, ops::RangeInclusive};
|
use std::{cmp::Ordering, ops::RangeInclusive};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![allow(clippy::needless_pass_by_value)] // False positives with `impl ToString`
|
#![expect(clippy::needless_pass_by_value)] // False positives with `impl ToString`
|
||||||
|
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
|
|||||||
@@ -759,7 +759,7 @@ impl TextEdit<'_> {
|
|||||||
ui.skip_ahead_auto_ids(1);
|
ui.skip_ahead_auto_ids(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
painter.galley(galley_pos, galley.clone(), text_color);
|
painter.galley(galley_pos, Arc::clone(&galley), text_color);
|
||||||
|
|
||||||
if has_focus && let Some(cursor_range) = state.cursor.range(&galley) {
|
if has_focus && let Some(cursor_range) = state.cursor.range(&galley) {
|
||||||
let primary_cursor_rect = cursor_rect(&galley, &cursor_range.primary, row_height)
|
let primary_cursor_rect = cursor_rect(&galley, &cursor_range.primary, row_height)
|
||||||
@@ -827,8 +827,7 @@ impl TextEdit<'_> {
|
|||||||
hint_text_str.as_str(),
|
hint_text_str.as_str(),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
} else if selection_changed {
|
} else if selection_changed && let Some(cursor_range) = cursor_range {
|
||||||
let cursor_range = cursor_range.unwrap();
|
|
||||||
let char_range = cursor_range.primary.index..=cursor_range.secondary.index;
|
let char_range = cursor_range.primary.index..=cursor_range.secondary.index;
|
||||||
let info = WidgetInfo::text_selection_changed(
|
let info = WidgetInfo::text_selection_changed(
|
||||||
ui.is_enabled(),
|
ui.is_enabled(),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![allow(clippy::undocumented_unsafe_blocks)]
|
#![expect(clippy::undocumented_unsafe_blocks)]
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![expect(clippy::unwrap_used)] // TODO(emilk): avoid unwraps
|
||||||
|
|
||||||
use std::num::NonZeroU64;
|
use std::num::NonZeroU64;
|
||||||
|
|
||||||
use eframe::{
|
use eframe::{
|
||||||
|
|||||||
@@ -459,11 +459,13 @@ fn call_after_delay(delay: std::time::Duration, f: impl FnOnce() + Send + 'stati
|
|||||||
std::thread::sleep(delay);
|
std::thread::sleep(delay);
|
||||||
f();
|
f();
|
||||||
})
|
})
|
||||||
.unwrap();
|
.expect("Failed to spawn a thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
fn call_after_delay(delay: std::time::Duration, f: impl FnOnce() + Send + 'static) {
|
fn call_after_delay(delay: std::time::Duration, f: impl FnOnce() + Send + 'static) {
|
||||||
|
#![expect(clippy::unwrap_used)]
|
||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
let window = web_sys::window().unwrap();
|
let window = web_sys::window().unwrap();
|
||||||
let closure = Closure::once(f);
|
let closure = Closure::once(f);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
//! Demo app for egui
|
//! Demo app for egui
|
||||||
#![allow(clippy::missing_errors_doc)]
|
|
||||||
|
|
||||||
mod apps;
|
mod apps;
|
||||||
mod backend_panel;
|
mod backend_panel;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
//! Demo app for egui
|
//! Demo app for egui
|
||||||
|
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||||
#![allow(rustdoc::missing_crate_level_docs)] // it's an example
|
#![expect(rustdoc::missing_crate_level_docs)] // it's an example
|
||||||
#![allow(clippy::never_loop)] // False positive
|
#![allow(clippy::allow_attributes, clippy::never_loop)]
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; // Much faster allocator, can give 20% speedups: https://github.com/emilk/egui/pull/7029
|
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; // Much faster allocator, can give 20% speedups: https://github.com/emilk/egui/pull/7029
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![allow(clippy::mem_forget)] // False positives from #[wasm_bindgen] macro
|
|
||||||
|
|
||||||
use eframe::wasm_bindgen::{self, prelude::*};
|
use eframe::wasm_bindgen::{self, prelude::*};
|
||||||
|
|
||||||
use crate::WrapApp;
|
use crate::WrapApp;
|
||||||
@@ -14,7 +12,7 @@ pub struct WebHandle {
|
|||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
impl WebHandle {
|
impl WebHandle {
|
||||||
/// Installs a panic hook, then returns.
|
/// Installs a panic hook, then returns.
|
||||||
#[allow(clippy::new_without_default, clippy::allow_attributes)]
|
#[allow(clippy::allow_attributes, clippy::new_without_default)]
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
// Redirect [`log`] message to `console.log` and friends:
|
// Redirect [`log`] message to `console.log` and friends:
|
||||||
@@ -31,6 +29,9 @@ impl WebHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Call this once from JavaScript to start your app.
|
/// Call this once from JavaScript to start your app.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns an error if the app could not start.
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn start(
|
pub async fn start(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ impl WrapApp {
|
|||||||
cc.egui_ctx
|
cc.egui_ctx
|
||||||
.add_plugin(crate::accessibility_inspector::AccessibilityInspectorPlugin::default());
|
.add_plugin(crate::accessibility_inspector::AccessibilityInspectorPlugin::default());
|
||||||
|
|
||||||
#[allow(unused_mut, clippy::allow_attributes)]
|
#[allow(clippy::allow_attributes, unused_mut)]
|
||||||
let mut slf = Self {
|
let mut slf = Self {
|
||||||
state: State::default(),
|
state: State::default(),
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:7051c34854469652d2d953f3110ebcf6fd60f8ee9a2b0c134d9f7255ef180ce5
|
oid sha256:5878bc5beaf4406c24f23d900aa9ac7c5507e44cb3ade83b743b8b62e7da1615
|
||||||
size 335353
|
size 335355
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:49823cfa4dfba54e54d0122f2bbb246c1daad2ca3ba15071c1ca44eeb3662855
|
oid sha256:706ad012e52a8c51175b050b985cca88e2cb306b24f618b7391641397d17cd28
|
||||||
size 92791
|
size 92804
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:1b65b6b1a3afe41337b8fe537525677284e49bd90be29cddb837787162ee452a
|
oid sha256:4135662f2b60a10ef8c3b155172d7a3edcf24a625d8286aeaad0614aa8819893
|
||||||
size 169596
|
size 169604
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:9c3af0c37a6997abe549dd28450c41d3d18bbc99d9577997d493566fbb7f9277
|
oid sha256:509020d8885b718900e534c9948cb95ae88e1eee9e113bdfb77a2f75b9a68f7b
|
||||||
size 96709
|
size 96703
|
||||||
|
|||||||
@@ -140,19 +140,19 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
|||||||
let ctx = egui::Context::default();
|
let ctx = egui::Context::default();
|
||||||
ctx.begin_pass(RawInput::default());
|
ctx.begin_pass(RawInput::default());
|
||||||
|
|
||||||
egui::CentralPanel::default().show(&ctx, |ui| {
|
let painter =
|
||||||
c.bench_function("Painter::rect", |b| {
|
egui::Painter::new(ctx.clone(), egui::LayerId::background(), ctx.content_rect());
|
||||||
let painter = ui.painter();
|
|
||||||
let rect = ui.max_rect();
|
c.bench_function("Painter::rect", |b| {
|
||||||
b.iter(|| {
|
let rect = painter.clip_rect();
|
||||||
painter.rect(
|
b.iter(|| {
|
||||||
rect,
|
painter.rect(
|
||||||
2.0,
|
rect,
|
||||||
egui::Color32::RED,
|
2.0,
|
||||||
(1.0, egui::Color32::WHITE),
|
egui::Color32::RED,
|
||||||
egui::StrokeKind::Inside,
|
(1.0, egui::Color32::WHITE),
|
||||||
);
|
egui::StrokeKind::Inside,
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -198,6 +198,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
|||||||
let mut string = String::new();
|
let mut string = String::new();
|
||||||
for _ in 0..NUM_LINES {
|
for _ in 0..NUM_LINES {
|
||||||
for i in 0..30_u8 {
|
for i in 0..30_u8 {
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
write!(string, "{i:02X} ").unwrap();
|
write!(string, "{i:02X} ").unwrap();
|
||||||
}
|
}
|
||||||
string.push('\n');
|
string.push('\n');
|
||||||
|
|||||||
@@ -8,14 +8,15 @@ impl crate::Demo for About {
|
|||||||
"About egui"
|
"About egui"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
egui::Window::new(self.name())
|
egui::Window::new(self.name())
|
||||||
.default_width(320.0)
|
.default_width(320.0)
|
||||||
.default_height(480.0)
|
.default_height(480.0)
|
||||||
.open(open)
|
.open(open)
|
||||||
.resizable([true, false])
|
.resizable([true, false])
|
||||||
.scroll(false)
|
.scroll(false)
|
||||||
.show(ctx, |ui| {
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
self.ui(ui);
|
self.ui(ui);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,12 +26,13 @@ impl crate::Demo for CodeEditor {
|
|||||||
"🖮 Code Editor"
|
"🖮 Code Editor"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
egui::Window::new(self.name())
|
egui::Window::new(self.name())
|
||||||
.open(open)
|
.open(open)
|
||||||
.default_height(500.0)
|
.default_height(500.0)
|
||||||
.show(ctx, |ui| self.ui(ui));
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| self.ui(ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ impl crate::Demo for CodeExample {
|
|||||||
"🖮 Code Example"
|
"🖮 Code Example"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
egui::Window::new(self.name())
|
egui::Window::new(self.name())
|
||||||
.open(open)
|
.open(open)
|
||||||
@@ -113,7 +113,8 @@ impl crate::Demo for CodeExample {
|
|||||||
.default_size([390.0, 500.0])
|
.default_size([390.0, 500.0])
|
||||||
.scroll(false)
|
.scroll(false)
|
||||||
.resizable([true, false]) // resizable so we can shrink if the text edit grows
|
.resizable([true, false]) // resizable so we can shrink if the text edit grows
|
||||||
.show(ctx, |ui| self.ui(ui));
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| self.ui(ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use egui::{
|
use egui::{
|
||||||
Color32, Context, Pos2, Rect, Ui,
|
Color32, Pos2, Rect, Ui,
|
||||||
containers::{Frame, Window},
|
containers::{Frame, Window},
|
||||||
emath, epaint,
|
emath, epaint,
|
||||||
epaint::PathStroke,
|
epaint::PathStroke,
|
||||||
@@ -18,13 +18,14 @@ impl crate::Demo for DancingStrings {
|
|||||||
"♫ Dancing Strings"
|
"♫ Dancing Strings"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut Ui, open: &mut bool) {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
Window::new(self.name())
|
Window::new(self.name())
|
||||||
.open(open)
|
.open(open)
|
||||||
.default_size(vec2(512.0, 256.0))
|
.default_size(vec2(512.0, 256.0))
|
||||||
.vscroll(false)
|
.vscroll(false)
|
||||||
.show(ctx, |ui| self.ui(ui));
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| self.ui(ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::View as _;
|
|||||||
use crate::is_mobile;
|
use crate::is_mobile;
|
||||||
use egui::containers::menu;
|
use egui::containers::menu;
|
||||||
use egui::style::StyleModifier;
|
use egui::style::StyleModifier;
|
||||||
use egui::{Context, Modifiers, ScrollArea, Ui};
|
use egui::{Modifiers, ScrollArea, Ui};
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
struct DemoGroup {
|
struct DemoGroup {
|
||||||
@@ -39,11 +39,11 @@ impl DemoGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn windows(&mut self, ctx: &Context, open: &mut BTreeSet<String>) {
|
pub fn windows(&mut self, ui: &mut Ui, open: &mut BTreeSet<String>) {
|
||||||
let Self { demos } = self;
|
let Self { demos } = self;
|
||||||
for demo in demos {
|
for demo in demos {
|
||||||
let mut is_open = open.contains(demo.name());
|
let mut is_open = open.contains(demo.name());
|
||||||
demo.show(ctx, &mut is_open);
|
demo.show(ui, &mut is_open);
|
||||||
set_open(open, demo.name(), is_open);
|
set_open(open, demo.name(), is_open);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ impl DemoGroups {
|
|||||||
tests.checkboxes(ui, open);
|
tests.checkboxes(ui, open);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn windows(&mut self, ctx: &Context, open: &mut BTreeSet<String>) {
|
pub fn windows(&mut self, ui: &mut Ui, open: &mut BTreeSet<String>) {
|
||||||
let Self {
|
let Self {
|
||||||
about,
|
about,
|
||||||
demos,
|
demos,
|
||||||
@@ -145,11 +145,11 @@ impl DemoGroups {
|
|||||||
} = self;
|
} = self;
|
||||||
{
|
{
|
||||||
let mut is_open = open.contains(about.name());
|
let mut is_open = open.contains(about.name());
|
||||||
about.show(ctx, &mut is_open);
|
about.show(ui, &mut is_open);
|
||||||
set_open(open, about.name(), is_open);
|
set_open(open, about.name(), is_open);
|
||||||
}
|
}
|
||||||
demos.windows(ctx, open);
|
demos.windows(ui, open);
|
||||||
tests.windows(ctx, open);
|
tests.windows(ui, open);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@ impl DemoWindows {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.mobile_top_bar(ui);
|
self.mobile_top_bar(ui);
|
||||||
self.groups.windows(ui.ctx(), &mut self.open);
|
self.groups.windows(ui, &mut self.open);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +295,7 @@ impl DemoWindows {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
self.groups.windows(ui.ctx(), &mut self.open);
|
self.groups.windows(ui, &mut self.open);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn demo_list_ui(&mut self, ui: &mut egui::Ui) {
|
fn demo_list_ui(&mut self, ui: &mut egui::Ui) {
|
||||||
@@ -372,8 +372,8 @@ fn file_menu_button(ui: &mut Ui) {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::{Demo as _, demo::demo_app_windows::DemoGroups};
|
use crate::{Demo as _, demo::demo_app_windows::DemoGroups};
|
||||||
|
|
||||||
use egui_kittest::kittest::Queryable as _;
|
use egui::Vec2;
|
||||||
use egui_kittest::{Harness, OsThreshold, SnapshotOptions, SnapshotResults};
|
use egui_kittest::{HarnessBuilder, OsThreshold, SnapshotOptions, SnapshotResults};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn demos_should_match_snapshot() {
|
fn demos_should_match_snapshot() {
|
||||||
@@ -394,20 +394,15 @@ mod tests {
|
|||||||
|
|
||||||
let name = remove_leading_emoji(demo.name());
|
let name = remove_leading_emoji(demo.name());
|
||||||
|
|
||||||
let mut harness = Harness::new_ui(|ui| {
|
let mut harness = HarnessBuilder::default()
|
||||||
egui_extras::install_image_loaders(ui);
|
.with_size(Vec2::splat(2048.0))
|
||||||
demo.show(ui, &mut true);
|
.build_ui(|ui| {
|
||||||
});
|
egui_extras::install_image_loaders(ui);
|
||||||
|
demo.show(ui, &mut true);
|
||||||
|
});
|
||||||
|
|
||||||
let window = harness
|
// Resize to fit every window, plus some margin:
|
||||||
.get_all_by_role(egui::accesskit::Role::Window)
|
harness.set_size(harness.ctx.globally_used_rect().max.to_vec2() + Vec2::splat(16.0));
|
||||||
.next()
|
|
||||||
.unwrap();
|
|
||||||
// TODO(lucasmerlin): Windows should probably have a label?
|
|
||||||
//let window = harness.get_by_label(name);
|
|
||||||
|
|
||||||
let size = window.rect().size();
|
|
||||||
harness.set_size(size);
|
|
||||||
|
|
||||||
// Run the app for some more frames...
|
// Run the app for some more frames...
|
||||||
harness.run_ok();
|
harness.run_ok();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use egui::{Color32, Context, Frame, Id, Ui, Window, vec2};
|
use egui::{Color32, Frame, Id, Ui, Window, vec2};
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
@@ -27,14 +27,15 @@ impl crate::Demo for DragAndDropDemo {
|
|||||||
"✋ Drag and Drop"
|
"✋ Drag and Drop"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
Window::new(self.name())
|
Window::new(self.name())
|
||||||
.open(open)
|
.open(open)
|
||||||
.default_size(vec2(256.0, 256.0))
|
.default_size(vec2(256.0, 256.0))
|
||||||
.vscroll(false)
|
.vscroll(false)
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.show(ctx, |ui| self.ui(ui));
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| self.ui(ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ impl crate::Demo for ExtraViewport {
|
|||||||
"🗖 Extra Viewport"
|
"🗖 Extra Viewport"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
if !*open {
|
if !*open {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = egui::Id::new(self.name());
|
let id = egui::Id::new(self.name());
|
||||||
|
|
||||||
ctx.show_viewport_immediate(
|
ui.show_viewport_immediate(
|
||||||
egui::ViewportId(id),
|
egui::ViewportId(id),
|
||||||
egui::ViewportBuilder::default()
|
egui::ViewportBuilder::default()
|
||||||
.with_title(self.name())
|
.with_title(self.name())
|
||||||
@@ -28,7 +28,7 @@ impl crate::Demo for ExtraViewport {
|
|||||||
ui.label("This egui integration does not support multiple viewports");
|
ui.label("This egui integration does not support multiple viewports");
|
||||||
} else {
|
} else {
|
||||||
egui::CentralPanel::default().show_inside(ui, |ui| {
|
egui::CentralPanel::default().show_inside(ui, |ui| {
|
||||||
viewport_content(ui, ctx, open);
|
viewport_content(ui, open);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -36,13 +36,13 @@ impl crate::Demo for ExtraViewport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn viewport_content(ui: &mut egui::Ui, ctx: &egui::Context, open: &mut bool) {
|
fn viewport_content(ui: &mut egui::Ui, open: &mut bool) {
|
||||||
ui.label("egui and eframe supports having multiple native windows like this, which egui calls 'viewports'.");
|
ui.label("egui and eframe supports having multiple native windows like this, which egui calls 'viewports'.");
|
||||||
|
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"This viewport has id: {:?}, child of viewport {:?}",
|
"This viewport has id: {:?}, child of viewport {:?}",
|
||||||
ctx.viewport_id(),
|
ui.viewport_id(),
|
||||||
ctx.parent_viewport_id()
|
ui.parent_viewport_id()
|
||||||
));
|
));
|
||||||
|
|
||||||
ui.label("Here you can see all the open viewports:");
|
ui.label("Here you can see all the open viewports:");
|
||||||
|
|||||||
@@ -28,11 +28,14 @@ impl crate::Demo for FontBook {
|
|||||||
"🔤 Font Book"
|
"🔤 Font Book"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
egui::Window::new(self.name()).open(open).show(ctx, |ui| {
|
egui::Window::new(self.name())
|
||||||
use crate::View as _;
|
.open(open)
|
||||||
self.ui(ui);
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
});
|
.show(ui, |ui| {
|
||||||
|
use crate::View as _;
|
||||||
|
self.ui(ui);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,11 +28,12 @@ impl crate::Demo for FrameDemo {
|
|||||||
"▣ Frame"
|
"▣ Frame"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
egui::Window::new(self.name())
|
egui::Window::new(self.name())
|
||||||
.open(open)
|
.open(open)
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.show(ctx, |ui| {
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
self.ui(ui);
|
self.ui(ui);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ impl crate::Demo for Highlighting {
|
|||||||
"✨ Highlighting"
|
"✨ Highlighting"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
egui::Window::new(self.name())
|
egui::Window::new(self.name())
|
||||||
.default_width(320.0)
|
.default_width(320.0)
|
||||||
.open(open)
|
.open(open)
|
||||||
.show(ctx, |ui| {
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
self.ui(ui);
|
self.ui(ui);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,12 +13,13 @@ impl crate::Demo for InteractiveContainerDemo {
|
|||||||
"\u{20E3} Interactive Container"
|
"\u{20E3} Interactive Container"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
egui::Window::new(self.name())
|
egui::Window::new(self.name())
|
||||||
.open(open)
|
.open(open)
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.default_width(250.0)
|
.default_width(250.0)
|
||||||
.show(ctx, |ui| {
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
self.ui(ui);
|
self.ui(ui);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{Demo, View};
|
use super::{Demo, View};
|
||||||
|
|
||||||
use egui::{
|
use egui::{
|
||||||
Align, Align2, Checkbox, CollapsingHeader, Color32, ComboBox, Context, FontId, Resize,
|
Align, Align2, Checkbox, CollapsingHeader, Color32, ComboBox, FontId, Resize, RichText, Sense,
|
||||||
RichText, Sense, Slider, Stroke, TextFormat, TextStyle, Ui, Vec2, Window, vec2,
|
Slider, Stroke, TextFormat, TextStyle, Ui, Vec2, Window, vec2,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Showcase some ui code
|
/// Showcase some ui code
|
||||||
@@ -47,12 +49,13 @@ impl Demo for MiscDemoWindow {
|
|||||||
"✨ Misc Demos"
|
"✨ Misc Demos"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
Window::new(self.name())
|
Window::new(self.name())
|
||||||
.open(open)
|
.open(open)
|
||||||
.vscroll(true)
|
.vscroll(true)
|
||||||
.hscroll(true)
|
.hscroll(true)
|
||||||
.show(ctx, |ui| self.ui(ui));
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| self.ui(ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,7 +521,7 @@ fn ui_stack_demo(ui: &mut Ui) {
|
|||||||
with various information.\n\nThis is how the stack looks like here:",
|
with various information.\n\nThis is how the stack looks like here:",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
let stack = ui.stack().clone();
|
let stack = Arc::clone(ui.stack());
|
||||||
egui::Frame::new()
|
egui::Frame::new()
|
||||||
.inner_margin(ui.spacing().menu_margin)
|
.inner_margin(ui.spacing().menu_margin)
|
||||||
.stroke(ui.visuals().widgets.noninteractive.bg_stroke)
|
.stroke(ui.visuals().widgets.noninteractive.bg_stroke)
|
||||||
|
|||||||
@@ -61,5 +61,5 @@ pub trait Demo {
|
|||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
|
|
||||||
/// Show windows, etc
|
/// Show windows, etc
|
||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool);
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use egui::{ComboBox, Context, Id, Modal, ProgressBar, Ui, Widget as _, Window};
|
use egui::{ComboBox, Id, Modal, ProgressBar, Ui, Widget as _, Window};
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(default))]
|
#[cfg_attr(feature = "serde", serde(default))]
|
||||||
@@ -32,13 +32,14 @@ impl crate::Demo for Modals {
|
|||||||
"🗖 Modals"
|
"🗖 Modals"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
Window::new(self.name())
|
Window::new(self.name())
|
||||||
.open(open)
|
.open(open)
|
||||||
.vscroll(false)
|
.vscroll(false)
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.show(ctx, |ui| self.ui(ui));
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| self.ui(ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,12 +27,13 @@ impl crate::Demo for MultiTouch {
|
|||||||
"👌 Multi Touch"
|
"👌 Multi Touch"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
egui::Window::new(self.name())
|
egui::Window::new(self.name())
|
||||||
.open(open)
|
.open(open)
|
||||||
.default_size(vec2(544.0, 512.0))
|
.default_size(vec2(544.0, 512.0))
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.show(ctx, |ui| {
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
self.ui(ui);
|
self.ui(ui);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use egui::{
|
use egui::{
|
||||||
Color32, Context, Frame, Grid, Pos2, Rect, Sense, Shape, Stroke, StrokeKind, Ui, Vec2,
|
Color32, Frame, Grid, Pos2, Rect, Sense, Shape, Stroke, StrokeKind, Ui, Vec2, Widget as _,
|
||||||
Widget as _, Window, emath,
|
Window, emath,
|
||||||
epaint::{self, CubicBezierShape, PathShape, QuadraticBezierShape},
|
epaint::{self, CubicBezierShape, PathShape, QuadraticBezierShape},
|
||||||
pos2,
|
pos2,
|
||||||
};
|
};
|
||||||
@@ -165,14 +165,15 @@ impl crate::Demo for PaintBezier {
|
|||||||
") Bézier Curve"
|
") Bézier Curve"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
Window::new(self.name())
|
Window::new(self.name())
|
||||||
.open(open)
|
.open(open)
|
||||||
.vscroll(false)
|
.vscroll(false)
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.default_size([300.0, 350.0])
|
.default_size([300.0, 350.0])
|
||||||
.show(ctx, |ui| self.ui(ui));
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| self.ui(ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use egui::{Color32, Context, Frame, Pos2, Rect, Sense, Stroke, Ui, Window, emath, vec2};
|
use egui::{Color32, Frame, Pos2, Rect, Sense, Stroke, Ui, Window, emath, vec2};
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(default))]
|
#[cfg_attr(feature = "serde", serde(default))]
|
||||||
@@ -77,13 +77,14 @@ impl crate::Demo for Painting {
|
|||||||
"🖊 Painting"
|
"🖊 Painting"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
Window::new(self.name())
|
Window::new(self.name())
|
||||||
.open(open)
|
.open(open)
|
||||||
.default_size(vec2(512.0, 512.0))
|
.default_size(vec2(512.0, 512.0))
|
||||||
.vscroll(false)
|
.vscroll(false)
|
||||||
.show(ctx, |ui| self.ui(ui));
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| self.ui(ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,15 @@ impl crate::Demo for Panels {
|
|||||||
"🗖 Panels"
|
"🗖 Panels"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
let window = egui::Window::new("Panels")
|
egui::Window::new("Panels")
|
||||||
.default_width(600.0)
|
.default_width(600.0)
|
||||||
.default_height(400.0)
|
.default_height(400.0)
|
||||||
.vscroll(false)
|
.vscroll(false)
|
||||||
.open(open);
|
.open(open)
|
||||||
window.show(ctx, |ui| self.ui(ui));
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| self.ui(ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -128,13 +128,14 @@ impl crate::Demo for PopupsDemo {
|
|||||||
"\u{2755} Popups"
|
"\u{2755} Popups"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
egui::Window::new(self.name())
|
egui::Window::new(self.name())
|
||||||
.open(open)
|
.open(open)
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.default_width(250.0)
|
.default_width(250.0)
|
||||||
.constrain(false)
|
.constrain(false)
|
||||||
.show(ctx, |ui| {
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
self.ui(ui);
|
self.ui(ui);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,14 +22,15 @@ impl crate::Demo for SceneDemo {
|
|||||||
"🔍 Scene"
|
"🔍 Scene"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
let window = egui::Window::new("Scene")
|
egui::Window::new("Scene")
|
||||||
.default_width(300.0)
|
.default_width(300.0)
|
||||||
.default_height(300.0)
|
.default_height(300.0)
|
||||||
.scroll(false)
|
.scroll(false)
|
||||||
.open(open);
|
.open(open)
|
||||||
window.show(ctx, |ui| self.ui(ui));
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| self.ui(ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,13 @@ impl crate::Demo for Screenshot {
|
|||||||
"📷 Screenshot"
|
"📷 Screenshot"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||||
egui::Window::new(self.name())
|
egui::Window::new(self.name())
|
||||||
.open(open)
|
.open(open)
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.default_width(250.0)
|
.default_width(250.0)
|
||||||
.show(ctx, |ui| {
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
|
.show(ui, |ui| {
|
||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
self.ui(ui);
|
self.ui(ui);
|
||||||
});
|
});
|
||||||
@@ -52,7 +53,7 @@ impl crate::View for Screenshot {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter_map(|e| {
|
.filter_map(|e| {
|
||||||
if let egui::Event::Screenshot { image, .. } = e {
|
if let egui::Event::Screenshot { image, .. } = e {
|
||||||
Some(image.clone())
|
Some(Arc::clone(image))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -62,7 +63,7 @@ impl crate::View for Screenshot {
|
|||||||
|
|
||||||
if let Some(image) = image {
|
if let Some(image) = image {
|
||||||
self.image = Some((
|
self.image = Some((
|
||||||
image.clone(),
|
Arc::clone(&image),
|
||||||
ui.ctx()
|
ui.ctx()
|
||||||
.load_texture("screenshot_demo", image, Default::default()),
|
.load_texture("screenshot_demo", image, Default::default()),
|
||||||
));
|
));
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user