mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 05:10:03 -04:00
Merge branch 'master' into wgpu-0.17
This commit is contained in:
4
.github/pull_request_template.md
vendored
4
.github/pull_request_template.md
vendored
@@ -4,8 +4,8 @@ Please read the "Making a PR" section of [`CONTRIBUTING.md`](https://github.com/
|
|||||||
* Keep your PR:s small and focused.
|
* Keep your PR:s small and focused.
|
||||||
* If applicable, add a screenshot or gif.
|
* If applicable, add a screenshot or gif.
|
||||||
* If it is a non-trivial addition, consider adding a demo for it to `egui_demo_lib`, or a new example.
|
* If it is a non-trivial addition, consider adding a demo for it to `egui_demo_lib`, or a new example.
|
||||||
* Do not open PR:s from your `master` branch, as thart makes it difficult for maintainers to add commits to your PR.
|
* Do NOT open PR:s from your `master` branch, as that makes it hard for maintainers to add commits to your PR.
|
||||||
* Remember to run `cargo fmt` and `cargo clippy`.
|
* Remember to run `cargo fmt` and `cargo cranky`.
|
||||||
* Open the PR as a draft until you have self-reviewed it and run `./scripts/check.sh`.
|
* Open the PR as a draft until you have self-reviewed it and run `./scripts/check.sh`.
|
||||||
* When you have addressed a PR comment, mark it as resolved.
|
* When you have addressed a PR comment, mark it as resolved.
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/labels.yml
vendored
2
.github/workflows/labels.yml
vendored
@@ -29,4 +29,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
mode: minimum
|
mode: minimum
|
||||||
count: 1
|
count: 1
|
||||||
labels: "ecolor, eframe, egui_extras, egui_glow, egui-wgpu, egui-winit, egui, epaint"
|
labels: "CI, dependencies, docs and examples, ecolor, eframe, egui_extras, egui_glow, egui-wgpu, egui-winit, egui, epaint, plot, typo"
|
||||||
|
|||||||
129
.github/workflows/rust.yml
vendored
129
.github/workflows/rust.yml
vendored
@@ -1,6 +1,6 @@
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
name: CI
|
name: Rust
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# web_sys_unstable_apis is required to enable the web_sys clipboard API which eframe web uses,
|
# web_sys_unstable_apis is required to enable the web_sys clipboard API which eframe web uses,
|
||||||
@@ -15,13 +15,11 @@ jobs:
|
|||||||
name: Format + check + test
|
name: Format + check + test
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
profile: default
|
toolchain: 1.67.0
|
||||||
toolchain: 1.65.0
|
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: Install packages (Linux)
|
- name: Install packages (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
@@ -37,10 +35,10 @@ jobs:
|
|||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
- name: Rustfmt
|
- name: Rustfmt
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo fmt --all -- --check
|
||||||
with:
|
|
||||||
command: fmt
|
- name: Lint vertical spacing
|
||||||
args: --all -- --check
|
run: ./scripts/lint.py
|
||||||
|
|
||||||
- name: Install cargo-cranky
|
- name: Install cargo-cranky
|
||||||
uses: baptiste0928/cargo-install@v1
|
uses: baptiste0928/cargo-install@v1
|
||||||
@@ -48,70 +46,37 @@ jobs:
|
|||||||
crate: cargo-cranky
|
crate: cargo-cranky
|
||||||
|
|
||||||
- name: check --all-features
|
- name: check --all-features
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo check --locked --all-features --all-targets
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
args: --locked --all-features --all-targets
|
|
||||||
|
|
||||||
- name: check egui_extras --all-features
|
- name: check egui_extras --all-features
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo check --locked --all-features --all-targets -p egui_extras
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
args: --locked --all-features --all-targets -p egui_extras
|
|
||||||
|
|
||||||
- name: check default features
|
- name: check default features
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo check --locked --all-targets
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
args: --locked --all-targets
|
|
||||||
|
|
||||||
- name: check --no-default-features
|
- name: check --no-default-features
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo check --locked --no-default-features --lib --all-targets
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
args: --locked --no-default-features --lib --all-targets
|
|
||||||
|
|
||||||
- name: check epaint --no-default-features
|
- name: check epaint --no-default-features
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo check --locked --no-default-features --lib --all-targets -p epaint
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
args: --locked --no-default-features --lib --all-targets -p epaint
|
|
||||||
|
|
||||||
- name: check eframe --no-default-features
|
- name: check eframe --no-default-features
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo check --locked --no-default-features --features x11 --lib --all-targets -p eframe
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
args: --locked --no-default-features --lib --all-targets -p eframe
|
|
||||||
|
|
||||||
- name: Test doc-tests
|
- name: Test doc-tests
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo test --doc --all-features
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
args: --doc --all-features
|
|
||||||
|
|
||||||
- name: cargo doc --lib
|
- name: cargo doc --lib
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo doc --lib --no-deps --all-features
|
||||||
with:
|
|
||||||
command: doc
|
|
||||||
args: --lib --no-deps --all-features
|
|
||||||
|
|
||||||
- name: cargo doc --document-private-items
|
- name: cargo doc --document-private-items
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo doc --document-private-items --no-deps --all-features
|
||||||
with:
|
|
||||||
command: doc
|
|
||||||
args: --document-private-items --no-deps --all-features
|
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo test --all-features
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
args: --all-features
|
|
||||||
|
|
||||||
- name: Cranky
|
- name: Cranky
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo cranky --all-targets --all-features -- -D warnings
|
||||||
with:
|
|
||||||
command: cranky
|
|
||||||
args: --all-targets --all-features -- -D warnings
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -119,13 +84,11 @@ jobs:
|
|||||||
name: Check wasm32 + wasm-bindgen
|
name: Check wasm32 + wasm-bindgen
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
toolchain: 1.67.0
|
||||||
toolchain: 1.65.0
|
targets: wasm32-unknown-unknown
|
||||||
target: wasm32-unknown-unknown
|
|
||||||
override: true
|
|
||||||
|
|
||||||
- run: sudo apt-get update && sudo apt-get install libgtk-3-dev
|
- run: sudo apt-get update && sudo apt-get install libgtk-3-dev
|
||||||
|
|
||||||
@@ -138,27 +101,18 @@ jobs:
|
|||||||
crate: cargo-cranky
|
crate: cargo-cranky
|
||||||
|
|
||||||
- name: Check wasm32 egui_demo_app
|
- name: Check wasm32 egui_demo_app
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo check -p egui_demo_app --lib --target wasm32-unknown-unknown
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
args: -p egui_demo_app --lib --target wasm32-unknown-unknown
|
|
||||||
|
|
||||||
- name: Check wasm32 egui_demo_app --all-features
|
- name: Check wasm32 egui_demo_app --all-features
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo check -p egui_demo_app --lib --target wasm32-unknown-unknown --all-features
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
args: -p egui_demo_app --lib --target wasm32-unknown-unknown --all-features
|
|
||||||
|
|
||||||
- name: Check wasm32 eframe
|
- name: Check wasm32 eframe
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo check -p eframe --lib --no-default-features --features glow,persistence --target wasm32-unknown-unknown
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
args: -p eframe --lib --no-default-features --features glow,persistence --target wasm32-unknown-unknown
|
|
||||||
|
|
||||||
- name: wasm-bindgen
|
- name: wasm-bindgen
|
||||||
uses: jetli/wasm-bindgen-action@v0.1.0
|
uses: jetli/wasm-bindgen-action@v0.1.0
|
||||||
with:
|
with:
|
||||||
version: "0.2.86"
|
version: "0.2.87"
|
||||||
|
|
||||||
- run: ./scripts/wasm_bindgen_check.sh --skip-setup
|
- run: ./scripts/wasm_bindgen_check.sh --skip-setup
|
||||||
|
|
||||||
@@ -188,13 +142,13 @@ jobs:
|
|||||||
name: cargo-deny ${{ matrix.target }}
|
name: cargo-deny ${{ matrix.target }}
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||||
with:
|
with:
|
||||||
rust-version: "1.65.0"
|
rust-version: "1.67.0"
|
||||||
log-level: error
|
log-level: error
|
||||||
command: check
|
command: check
|
||||||
arguments: ${{ matrix.flags }} --target ${{ matrix.target }}
|
arguments: --target ${{ matrix.target }}
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -202,14 +156,12 @@ jobs:
|
|||||||
name: android
|
name: android
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
toolchain: 1.67.0
|
||||||
toolchain: 1.65.0
|
targets: aarch64-linux-android
|
||||||
target: aarch64-linux-android
|
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: Set up cargo cache
|
- name: Set up cargo cache
|
||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
@@ -223,18 +175,13 @@ jobs:
|
|||||||
name: Check Windows
|
name: Check Windows
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
toolchain: 1.67.0
|
||||||
toolchain: 1.65.0
|
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: Set up cargo cache
|
- name: Set up cargo cache
|
||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
- name: Check
|
- name: Check
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo check --all-targets --all-features
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
args: --all-targets --all-features
|
|
||||||
|
|||||||
2
.github/workflows/typos.yml
vendored
2
.github/workflows/typos.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Actions Repository
|
- name: Checkout Actions Repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Check spelling of entire workspace
|
- name: Check spelling of entire workspace
|
||||||
uses: crate-ci/typos@master
|
uses: crate-ci/typos@master
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ All notable changes to the `egui` crate will be documented in this file.
|
|||||||
|
|
||||||
NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG.md), [`egui-winit`](crates/egui-winit/CHANGELOG.md), [`egui_glium`](crates/egui_glium/CHANGELOG.md), [`egui_glow`](crates/egui_glow/CHANGELOG.md) and [`egui-wgpu`](crates/egui-wgpu/CHANGELOG.md) have their own changelogs!
|
NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG.md), [`egui-winit`](crates/egui-winit/CHANGELOG.md), [`egui_glium`](crates/egui_glium/CHANGELOG.md), [`egui_glow`](crates/egui_glow/CHANGELOG.md) and [`egui-wgpu`](crates/egui-wgpu/CHANGELOG.md) have their own changelogs!
|
||||||
|
|
||||||
|
This file is updated upon each release.
|
||||||
## Unreleased
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
## 0.22.0 - 2023-05-23 - A plethora of small improvements
|
## 0.22.0 - 2023-05-23 - A plethora of small improvements
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ Read the section on integrations at <https://github.com/emilk/egui#integrations>
|
|||||||
## Code Conventions
|
## Code Conventions
|
||||||
Conventions unless otherwise specified:
|
Conventions unless otherwise specified:
|
||||||
|
|
||||||
* angles are in radians
|
* angles are in radians and clock-wise
|
||||||
* `Vec2::X` is right and `Vec2::Y` is down.
|
* `Vec2::X` is right and `Vec2::Y` is down.
|
||||||
* `Pos2::ZERO` is left top.
|
* `Pos2::ZERO` is left top.
|
||||||
|
|
||||||
|
|||||||
21
Cargo.lock
generated
21
Cargo.lock
generated
@@ -326,12 +326,6 @@ dependencies = [
|
|||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "atomic_refcell"
|
|
||||||
version = "0.1.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "79d6dc922a2792b006573f60b2648076355daeae5ce9cb59507e5908c9625d31"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atspi"
|
name = "atspi"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
@@ -1394,7 +1388,6 @@ version = "0.22.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"ab_glyph",
|
"ab_glyph",
|
||||||
"ahash 0.8.3",
|
"ahash 0.8.3",
|
||||||
"atomic_refcell",
|
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"criterion",
|
"criterion",
|
||||||
@@ -3130,6 +3123,16 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "save_plot"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"eframe",
|
||||||
|
"env_logger",
|
||||||
|
"image",
|
||||||
|
"rfd",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -4492,9 +4495,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xml-rs"
|
name = "xml-rs"
|
||||||
version = "0.8.13"
|
version = "0.8.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2d8f380ae16a37b30e6a2cf67040608071384b1450c189e61bea3ff57cde922d"
|
checksum = "5a56c84a8ccd4258aed21c92f70c0f6dea75356b6892ae27c24139da456f9336"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xmlparser"
|
name = "xmlparser"
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ warn = [
|
|||||||
"clippy::trailing_empty_array",
|
"clippy::trailing_empty_array",
|
||||||
"clippy::trait_duplication_in_bounds",
|
"clippy::trait_duplication_in_bounds",
|
||||||
"clippy::unimplemented",
|
"clippy::unimplemented",
|
||||||
|
"clippy::uninlined_format_args",
|
||||||
"clippy::unnecessary_wraps",
|
"clippy::unnecessary_wraps",
|
||||||
"clippy::unnested_or_patterns",
|
"clippy::unnested_or_patterns",
|
||||||
"clippy::unused_peekable",
|
"clippy::unused_peekable",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# There is also a scripts/clippy_wasm/clippy.toml which forbids some mthods that are not available in wasm.
|
# There is also a scripts/clippy_wasm/clippy.toml which forbids some mthods that are not available in wasm.
|
||||||
|
|
||||||
msrv = "1.65"
|
msrv = "1.67"
|
||||||
|
|
||||||
# Allow-list of words for markdown in dosctrings https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
|
# Allow-list of words for markdown in dosctrings https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
|
||||||
doc-valid-idents = [
|
doc-valid-idents = [
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
All notable changes to the `ecolor` crate will be noted in this file.
|
All notable changes to the `ecolor` crate will be noted in this file.
|
||||||
|
|
||||||
|
|
||||||
## Unreleased
|
This file is updated upon each release.
|
||||||
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
## 0.22.0 - 2023-05-23
|
## 0.22.0 - 2023-05-23
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ authors = [
|
|||||||
]
|
]
|
||||||
description = "Color structs and color conversion utilities"
|
description = "Color structs and color conversion utilities"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.67"
|
||||||
homepage = "https://github.com/emilk/egui"
|
homepage = "https://github.com/emilk/egui"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ All notable changes to the `eframe` crate.
|
|||||||
|
|
||||||
NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/CHANGELOG.md), [`egui_glow`](../egui_glow/CHANGELOG.md),and [`egui-wgpu`](../egui-wgpu/CHANGELOG.md) have their own changelogs!
|
NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/CHANGELOG.md), [`egui_glow`](../egui_glow/CHANGELOG.md),and [`egui-wgpu`](../egui-wgpu/CHANGELOG.md) have their own changelogs!
|
||||||
|
|
||||||
|
This file is updated upon each release.
|
||||||
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
## Unreleased
|
|
||||||
* Expose raw window and display handles in `CreationContext` and `Frame`
|
|
||||||
|
|
||||||
## 0.22.0 - 2023-05-23
|
## 0.22.0 - 2023-05-23
|
||||||
* Fix: `request_repaint_after` works even when called from background thread [#2939](https://github.com/emilk/egui/pull/2939)
|
* Fix: `request_repaint_after` works even when called from background thread [#2939](https://github.com/emilk/egui/pull/2939)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ version = "0.22.0"
|
|||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "egui framework - write GUI apps that compiles to web and/or natively"
|
description = "egui framework - write GUI apps that compiles to web and/or natively"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.67"
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/eframe"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/eframe"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -27,7 +27,14 @@ targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"]
|
|||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["accesskit", "default_fonts", "glow"]
|
default = [
|
||||||
|
"accesskit",
|
||||||
|
"default_fonts",
|
||||||
|
"glow",
|
||||||
|
"wayland",
|
||||||
|
"winit/default",
|
||||||
|
"x11",
|
||||||
|
]
|
||||||
|
|
||||||
## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/).
|
## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/).
|
||||||
accesskit = ["egui/accesskit", "egui-winit/accesskit"]
|
accesskit = ["egui/accesskit", "egui-winit/accesskit"]
|
||||||
@@ -42,6 +49,9 @@ glow = ["dep:glow", "dep:egui_glow", "dep:glutin", "dep:glutin-winit"]
|
|||||||
## Enables wayland support and fixes clipboard issue.
|
## Enables wayland support and fixes clipboard issue.
|
||||||
wayland = ["egui-winit/wayland"]
|
wayland = ["egui-winit/wayland"]
|
||||||
|
|
||||||
|
## Enables compiling for x11.
|
||||||
|
x11 = ["egui-winit/x11"]
|
||||||
|
|
||||||
## Enable saving app state to disk.
|
## Enable saving app state to disk.
|
||||||
persistence = [
|
persistence = [
|
||||||
"directories-next",
|
"directories-next",
|
||||||
@@ -109,7 +119,7 @@ image = { version = "0.24", default-features = false, features = [
|
|||||||
"png",
|
"png",
|
||||||
] } # Needed for app icon
|
] } # Needed for app icon
|
||||||
raw-window-handle = { version = "0.5.0" }
|
raw-window-handle = { version = "0.5.0" }
|
||||||
winit = "0.28.1"
|
winit = { version = "0.28.1", default-features = false }
|
||||||
|
|
||||||
# optional native:
|
# optional native:
|
||||||
directories-next = { version = "2", optional = true }
|
directories-next = { version = "2", optional = true }
|
||||||
|
|||||||
@@ -431,7 +431,7 @@ pub struct NativeOptions {
|
|||||||
/// will be used instead.
|
/// will be used instead.
|
||||||
///
|
///
|
||||||
/// ### On Wayland
|
/// ### On Wayland
|
||||||
/// On Wauland this sets the Application ID for the window.
|
/// On Wayland this sets the Application ID for the window.
|
||||||
///
|
///
|
||||||
/// The application ID is used in several places of the compositor, e.g. for
|
/// The application ID is used in several places of the compositor, e.g. for
|
||||||
/// grouping windows of the same application. It is also important for
|
/// grouping windows of the same application. It is also important for
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
//! To learn how to set up `eframe` for web and native, go to <https://github.com/emilk/eframe_template/> and follow the instructions there!
|
//! To learn how to set up `eframe` for web and native, go to <https://github.com/emilk/eframe_template/> and follow the instructions there!
|
||||||
//!
|
//!
|
||||||
//! In short, you implement [`App`] (especially [`App::update`]) and then
|
//! In short, you implement [`App`] (especially [`App::update`]) and then
|
||||||
//! call [`crate::run_native`] from your `main.rs`, and/or call `eframe::start_web` from your `lib.rs`.
|
//! call [`crate::run_native`] from your `main.rs`, and/or use `eframe::WebRunner` from your `lib.rs`.
|
||||||
//!
|
//!
|
||||||
//! ## Usage, native:
|
//! ## Usage, native:
|
||||||
//! ``` no_run
|
//! ``` no_run
|
||||||
@@ -272,6 +272,7 @@ pub fn run_simple_native(
|
|||||||
struct SimpleApp<U> {
|
struct SimpleApp<U> {
|
||||||
update_fun: U,
|
update_fun: U,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<U: FnMut(&egui::Context, &mut Frame)> App for SimpleApp<U> {
|
impl<U: FnMut(&egui::Context, &mut Frame)> App for SimpleApp<U> {
|
||||||
fn update(&mut self, ctx: &egui::Context, frame: &mut Frame) {
|
fn update(&mut self, ctx: &egui::Context, frame: &mut Frame) {
|
||||||
(self.update_fun)(ctx, frame);
|
(self.update_fun)(ctx, frame);
|
||||||
|
|||||||
@@ -121,9 +121,12 @@ pub fn window_builder<E>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||||
if let Some(app_id) = &native_options.app_id {
|
{
|
||||||
use winit::platform::wayland::WindowBuilderExtWayland as _;
|
use winit::platform::wayland::WindowBuilderExtWayland as _;
|
||||||
window_builder = window_builder.with_name(app_id, "");
|
match &native_options.app_id {
|
||||||
|
Some(app_id) => window_builder = window_builder.with_name(app_id, ""),
|
||||||
|
None => window_builder = window_builder.with_name(title, ""),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(min_size) = *min_window_size {
|
if let Some(min_size) = *min_window_size {
|
||||||
@@ -332,8 +335,10 @@ pub struct EpiIntegration {
|
|||||||
pub egui_ctx: egui::Context,
|
pub egui_ctx: egui::Context,
|
||||||
pending_full_output: egui::FullOutput,
|
pending_full_output: egui::FullOutput,
|
||||||
egui_winit: egui_winit::State,
|
egui_winit: egui_winit::State,
|
||||||
|
|
||||||
/// When set, it is time to close the native window.
|
/// When set, it is time to close the native window.
|
||||||
close: bool,
|
close: bool,
|
||||||
|
|
||||||
can_drag_window: bool,
|
can_drag_window: bool,
|
||||||
window_state: WindowState,
|
window_state: WindowState,
|
||||||
follow_system_theme: bool,
|
follow_system_theme: bool,
|
||||||
@@ -562,24 +567,26 @@ impl EpiIntegration {
|
|||||||
pub fn maybe_autosave(&mut self, app: &mut dyn epi::App, window: &winit::window::Window) {
|
pub fn maybe_autosave(&mut self, app: &mut dyn epi::App, window: &winit::window::Window) {
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
if now - self.last_auto_save > app.auto_save_interval() {
|
if now - self.last_auto_save > app.auto_save_interval() {
|
||||||
self.save(app, window);
|
self.save(app, Some(window));
|
||||||
self.last_auto_save = now;
|
self.last_auto_save = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
pub fn save(&mut self, _app: &mut dyn epi::App, _window: &winit::window::Window) {
|
pub fn save(&mut self, _app: &mut dyn epi::App, _window: Option<&winit::window::Window>) {
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
if let Some(storage) = self.frame.storage_mut() {
|
if let Some(storage) = self.frame.storage_mut() {
|
||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
|
|
||||||
if _app.persist_native_window() {
|
if let Some(window) = _window {
|
||||||
crate::profile_scope!("native_window");
|
if _app.persist_native_window() {
|
||||||
epi::set_value(
|
crate::profile_scope!("native_window");
|
||||||
storage,
|
epi::set_value(
|
||||||
STORAGE_WINDOW_KEY,
|
storage,
|
||||||
&WindowSettings::from_display(_window),
|
STORAGE_WINDOW_KEY,
|
||||||
);
|
&WindowSettings::from_display(window),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if _app.persist_egui_memory() {
|
if _app.persist_egui_memory() {
|
||||||
crate::profile_scope!("egui_memory");
|
crate::profile_scope!("egui_memory");
|
||||||
|
|||||||
@@ -80,14 +80,45 @@ impl crate::Storage for FileStorage {
|
|||||||
join_handle.join().ok();
|
join_handle.join().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
let join_handle = std::thread::spawn(move || {
|
match std::thread::Builder::new()
|
||||||
let file = std::fs::File::create(&file_path).unwrap();
|
.name("eframe_persist".to_owned())
|
||||||
let config = Default::default();
|
.spawn(move || {
|
||||||
ron::ser::to_writer_pretty(file, &kv, config).unwrap();
|
save_to_disk(&file_path, &kv);
|
||||||
log::trace!("Persisted to {:?}", file_path);
|
}) {
|
||||||
});
|
Ok(join_handle) => {
|
||||||
|
self.last_save_join_handle = Some(join_handle);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Failed to spawn thread to save app state: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.last_save_join_handle = Some(join_handle);
|
fn save_to_disk(file_path: &PathBuf, kv: &HashMap<String, String>) {
|
||||||
|
crate::profile_function!();
|
||||||
|
|
||||||
|
if let Some(parent_dir) = file_path.parent() {
|
||||||
|
if !parent_dir.exists() {
|
||||||
|
if let Err(err) = std::fs::create_dir_all(parent_dir) {
|
||||||
|
log::warn!("Failed to create directory {parent_dir:?}: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match std::fs::File::create(file_path) {
|
||||||
|
Ok(file) => {
|
||||||
|
let config = Default::default();
|
||||||
|
|
||||||
|
if let Err(err) = ron::ser::to_writer_pretty(file, &kv, config) {
|
||||||
|
log::warn!("Failed to serialize app state: {err}");
|
||||||
|
} else {
|
||||||
|
log::trace!("Persisted to {:?}", file_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Failed to create file {file_path:?}: {err}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use super::epi_integration::{self, EpiIntegration};
|
|||||||
pub enum UserEvent {
|
pub enum UserEvent {
|
||||||
RequestRepaint {
|
RequestRepaint {
|
||||||
when: Instant,
|
when: Instant,
|
||||||
|
|
||||||
/// What the frame number was when the repaint was _requested_.
|
/// What the frame number was when the repaint was _requested_.
|
||||||
frame_nr: u64,
|
frame_nr: u64,
|
||||||
},
|
},
|
||||||
@@ -144,11 +145,13 @@ fn run_and_return(
|
|||||||
// Platform-dependent event handlers to workaround a winit bug
|
// Platform-dependent event handlers to workaround a winit bug
|
||||||
// See: https://github.com/rust-windowing/winit/issues/987
|
// See: https://github.com/rust-windowing/winit/issues/987
|
||||||
// See: https://github.com/rust-windowing/winit/issues/1619
|
// See: https://github.com/rust-windowing/winit/issues/1619
|
||||||
winit::event::Event::RedrawEventsCleared if cfg!(windows) => {
|
#[cfg(windows)]
|
||||||
|
winit::event::Event::RedrawEventsCleared => {
|
||||||
next_repaint_time = extremely_far_future();
|
next_repaint_time = extremely_far_future();
|
||||||
winit_app.run_ui_and_paint()
|
winit_app.run_ui_and_paint()
|
||||||
}
|
}
|
||||||
winit::event::Event::RedrawRequested(_) if !cfg!(windows) => {
|
#[cfg(not(windows))]
|
||||||
|
winit::event::Event::RedrawRequested(_) => {
|
||||||
next_repaint_time = extremely_far_future();
|
next_repaint_time = extremely_far_future();
|
||||||
winit_app.run_ui_and_paint()
|
winit_app.run_ui_and_paint()
|
||||||
}
|
}
|
||||||
@@ -705,7 +708,7 @@ mod glow_integration {
|
|||||||
|
|
||||||
let painter =
|
let painter =
|
||||||
egui_glow::Painter::new(gl.clone(), "", self.native_options.shader_version)
|
egui_glow::Painter::new(gl.clone(), "", self.native_options.shader_version)
|
||||||
.unwrap_or_else(|error| panic!("some OpenGL error occurred {}\n", error));
|
.unwrap_or_else(|err| panic!("An OpenGL error occurred: {err}\n"));
|
||||||
|
|
||||||
let system_theme = system_theme(gl_window.window(), &self.native_options);
|
let system_theme = system_theme(gl_window.window(), &self.native_options);
|
||||||
let mut integration = epi_integration::EpiIntegration::new(
|
let mut integration = epi_integration::EpiIntegration::new(
|
||||||
@@ -799,7 +802,7 @@ mod glow_integration {
|
|||||||
if let Some(mut running) = self.running.take() {
|
if let Some(mut running) = self.running.take() {
|
||||||
running
|
running
|
||||||
.integration
|
.integration
|
||||||
.save(running.app.as_mut(), running.gl_window.window());
|
.save(running.app.as_mut(), running.gl_window.window.as_ref());
|
||||||
running.app.on_exit(Some(&running.gl));
|
running.app.on_exit(Some(&running.gl));
|
||||||
running.painter.destroy();
|
running.painter.destroy();
|
||||||
}
|
}
|
||||||
@@ -1258,9 +1261,9 @@ mod wgpu_integration {
|
|||||||
|
|
||||||
fn save_and_destroy(&mut self) {
|
fn save_and_destroy(&mut self) {
|
||||||
if let Some(mut running) = self.running.take() {
|
if let Some(mut running) = self.running.take() {
|
||||||
if let Some(window) = &self.window {
|
running
|
||||||
running.integration.save(running.app.as_mut(), window);
|
.integration
|
||||||
}
|
.save(running.app.as_mut(), self.window.as_ref());
|
||||||
|
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
running.app.on_exit(None);
|
running.app.on_exit(None);
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ pub fn canvas_element(canvas_id: &str) -> Option<web_sys::HtmlCanvasElement> {
|
|||||||
|
|
||||||
pub fn canvas_element_or_die(canvas_id: &str) -> web_sys::HtmlCanvasElement {
|
pub fn canvas_element_or_die(canvas_id: &str) -> web_sys::HtmlCanvasElement {
|
||||||
canvas_element(canvas_id)
|
canvas_element(canvas_id)
|
||||||
.unwrap_or_else(|| panic!("Failed to find canvas with id {:?}", canvas_id))
|
.unwrap_or_else(|| panic!("Failed to find canvas with id {canvas_id:?}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn canvas_origin(canvas_id: &str) -> egui::Pos2 {
|
fn canvas_origin(canvas_id: &str) -> egui::Pos2 {
|
||||||
|
|||||||
@@ -104,8 +104,7 @@ pub fn install_text_agent(runner_ref: &WebRunner) -> Result<(), JsValue> {
|
|||||||
runner_ref.add_event_listener(&input, "focusout", move |_event: web_sys::MouseEvent, _| {
|
runner_ref.add_event_listener(&input, "focusout", move |_event: web_sys::MouseEvent, _| {
|
||||||
// Delay 10 ms, and focus again.
|
// Delay 10 ms, and focus again.
|
||||||
let func = js_sys::Function::new_no_args(&format!(
|
let func = js_sys::Function::new_no_args(&format!(
|
||||||
"document.getElementById('{}').focus()",
|
"document.getElementById('{AGENT_ID}').focus()"
|
||||||
AGENT_ID
|
|
||||||
));
|
));
|
||||||
window
|
window
|
||||||
.set_timeout_with_callback_and_timeout_and_arguments_0(&func, 10)
|
.set_timeout_with_callback_and_timeout_and_arguments_0(&func, 10)
|
||||||
@@ -221,8 +220,8 @@ pub fn move_text_cursor(cursor: Option<egui::Pos2>, canvas_id: &str) -> Option<(
|
|||||||
let x = (x - canvas.offset_width() as f32 / 2.0)
|
let x = (x - canvas.offset_width() as f32 / 2.0)
|
||||||
.min(canvas.client_width() as f32 - bounding_rect.width() as f32);
|
.min(canvas.client_width() as f32 - bounding_rect.width() as f32);
|
||||||
style.set_property("position", "absolute").ok()?;
|
style.set_property("position", "absolute").ok()?;
|
||||||
style.set_property("top", &format!("{}px", y)).ok()?;
|
style.set_property("top", &format!("{y}px")).ok()?;
|
||||||
style.set_property("left", &format!("{}px", x)).ok()
|
style.set_property("left", &format!("{x}px")).ok()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
style.set_property("position", "absolute").ok()?;
|
style.set_property("position", "absolute").ok()?;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ impl WebPainterGlow {
|
|||||||
let gl = std::sync::Arc::new(gl);
|
let gl = std::sync::Arc::new(gl);
|
||||||
|
|
||||||
let painter = egui_glow::Painter::new(gl, shader_prefix, None)
|
let painter = egui_glow::Painter::new(gl, shader_prefix, None)
|
||||||
.map_err(|error| format!("Error starting glow painter: {}", error))?;
|
.map_err(|err| format!("Error starting glow painter: {err}"))?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
canvas,
|
canvas,
|
||||||
|
|||||||
@@ -87,8 +87,7 @@ impl WebPainterWgpu {
|
|||||||
} else {
|
} else {
|
||||||
// Workaround for https://github.com/gfx-rs/wgpu/issues/3710:
|
// Workaround for https://github.com/gfx-rs/wgpu/issues/3710:
|
||||||
// Don't use `create_surface_from_canvas`, but `create_surface` instead!
|
// Don't use `create_surface_from_canvas`, but `create_surface` instead!
|
||||||
let raw_window =
|
let raw_window = EguiWebWindow(egui::util::hash(("egui on wgpu", canvas_id)) as u32);
|
||||||
EguiWebWindow(egui::util::hash(&format!("egui on wgpu {canvas_id}")) as u32);
|
|
||||||
canvas.set_attribute("data-raw-handle", &raw_window.0.to_string());
|
canvas.set_attribute("data-raw-handle", &raw_window.0.to_string());
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
All notable changes to the `egui-wgpu` integration will be noted in this file.
|
All notable changes to the `egui-wgpu` integration will be noted in this file.
|
||||||
|
|
||||||
|
|
||||||
## Unreleased
|
This file is updated upon each release.
|
||||||
* Fix panic on wgpu GL backend due to new screenshot capability ([#3068](https://github.com/emilk/egui/issues/3068), [#3078](https://github.com/emilk/egui/pull/3078)
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
## 0.22.0 - 2023-05-23
|
## 0.22.0 - 2023-05-23
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ authors = [
|
|||||||
"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>",
|
"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>",
|
||||||
]
|
]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.67"
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/egui-wgpu"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/egui-wgpu"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -50,7 +50,7 @@ wgpu.workspace = true
|
|||||||
## Enable this when generating docs.
|
## Enable this when generating docs.
|
||||||
document-features = { version = "0.2", optional = true }
|
document-features = { version = "0.2", optional = true }
|
||||||
|
|
||||||
winit = { version = "0.28", optional = true }
|
winit = { version = "0.28", default-features = false, optional = true }
|
||||||
|
|
||||||
# Native:
|
# Native:
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
|
|||||||
@@ -560,7 +560,7 @@ impl Renderer {
|
|||||||
} else {
|
} else {
|
||||||
// allocate a new texture
|
// allocate a new texture
|
||||||
// Use same label for all resources associated with this texture id (no point in retyping the type)
|
// Use same label for all resources associated with this texture id (no point in retyping the type)
|
||||||
let label_str = format!("egui_texid_{:?}", id);
|
let label_str = format!("egui_texid_{id:?}");
|
||||||
let label = Some(label_str.as_str());
|
let label = Some(label_str.as_str());
|
||||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||||
label,
|
label,
|
||||||
@@ -904,8 +904,7 @@ fn create_sampler(
|
|||||||
};
|
};
|
||||||
device.create_sampler(&wgpu::SamplerDescriptor {
|
device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
label: Some(&format!(
|
label: Some(&format!(
|
||||||
"egui sampler (mag: {:?}, min {:?})",
|
"egui sampler (mag: {mag_filter:?}, min {min_filter:?})"
|
||||||
mag_filter, min_filter
|
|
||||||
)),
|
)),
|
||||||
mag_filter,
|
mag_filter,
|
||||||
min_filter,
|
min_filter,
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ impl Painter {
|
|||||||
depth_or_array_layers: 1,
|
depth_or_array_layers: 1,
|
||||||
},
|
},
|
||||||
mip_level_count: 1,
|
mip_level_count: 1,
|
||||||
sample_count: 1,
|
sample_count: self.msaa_samples,
|
||||||
dimension: wgpu::TextureDimension::D2,
|
dimension: wgpu::TextureDimension::D2,
|
||||||
format: depth_format,
|
format: depth_format,
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# Changelog for egui-winit
|
# Changelog for egui-winit
|
||||||
All notable changes to the `egui-winit` integration will be noted in this file.
|
All notable changes to the `egui-winit` integration will be noted in this file.
|
||||||
|
|
||||||
|
This file is updated upon each release.
|
||||||
## Unreleased
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
## 0.22.0 - 2023-05-23
|
## 0.22.0 - 2023-05-23
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ version = "0.22.0"
|
|||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "Bindings for using egui with winit"
|
description = "Bindings for using egui with winit"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.67"
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/egui-winit"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/egui-winit"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -18,7 +18,7 @@ all-features = true
|
|||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["clipboard", "links", "wayland", "winit/default"]
|
default = ["clipboard", "links", "wayland", "winit/default", "x11"]
|
||||||
|
|
||||||
## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/).
|
## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/).
|
||||||
accesskit = ["accesskit_winit", "egui/accesskit"]
|
accesskit = ["accesskit_winit", "egui/accesskit"]
|
||||||
@@ -42,6 +42,9 @@ serde = ["egui/serde", "dep:serde"]
|
|||||||
## Enables Wayland support.
|
## Enables Wayland support.
|
||||||
wayland = ["winit/wayland"]
|
wayland = ["winit/wayland"]
|
||||||
|
|
||||||
|
## Enables compiling for x11.
|
||||||
|
x11 = ["winit/x11"]
|
||||||
|
|
||||||
# Allow crates to choose an android-activity backend via Winit
|
# Allow crates to choose an android-activity backend via Winit
|
||||||
# - It's important that most applications should not have to depend on android-activity directly, and can
|
# - It's important that most applications should not have to depend on android-activity directly, and can
|
||||||
# rely on Winit to pull in a suitable version (unlike most Rust crates, any version conflicts won't link)
|
# rely on Winit to pull in a suitable version (unlike most Rust crates, any version conflicts won't link)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use raw_window_handle::HasRawDisplayHandle;
|
|||||||
/// Handles interfacing with the OS clipboard.
|
/// Handles interfacing with the OS clipboard.
|
||||||
///
|
///
|
||||||
/// If the "clipboard" feature is off, or we cannot connect to the OS clipboard,
|
/// If the "clipboard" feature is off, or we cannot connect to the OS clipboard,
|
||||||
/// then a fallback clipboard that just works works within the same app is used instead.
|
/// then a fallback clipboard that just works within the same app is used instead.
|
||||||
pub struct Clipboard {
|
pub struct Clipboard {
|
||||||
#[cfg(all(feature = "arboard", not(target_os = "android")))]
|
#[cfg(all(feature = "arboard", not(target_os = "android")))]
|
||||||
arboard: Option<arboard::Clipboard>,
|
arboard: Option<arboard::Clipboard>,
|
||||||
|
|||||||
@@ -307,6 +307,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
WindowEvent::KeyboardInput { input, .. } => {
|
WindowEvent::KeyboardInput { input, .. } => {
|
||||||
self.on_keyboard_input(input);
|
self.on_keyboard_input(input);
|
||||||
|
// When pressing the Tab key, egui focuses the first focusable element, hence Tab always consumes.
|
||||||
let consumed = egui_ctx.wants_keyboard_input()
|
let consumed = egui_ctx.wants_keyboard_input()
|
||||||
|| input.virtual_keycode == Some(winit::event::VirtualKeyCode::Tab);
|
|| input.virtual_keycode == Some(winit::event::VirtualKeyCode::Tab);
|
||||||
EventResponse {
|
EventResponse {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ version = "0.22.0"
|
|||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "An easy-to-use immediate mode GUI that runs on both web and native"
|
description = "An easy-to-use immediate mode GUI that runs on both web and native"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.67"
|
||||||
homepage = "https://github.com/emilk/egui"
|
homepage = "https://github.com/emilk/egui"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
readme = "../../README.md"
|
readme = "../../README.md"
|
||||||
|
|||||||
@@ -426,7 +426,7 @@ impl Prepared {
|
|||||||
temporarily_invisible: _,
|
temporarily_invisible: _,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
state.size = content_ui.min_rect().size();
|
state.size = content_ui.min_size();
|
||||||
|
|
||||||
ctx.memory_mut(|m| m.areas.set_state(layer_id, state));
|
ctx.memory_mut(|m| m.areas.set_state(layer_id, state));
|
||||||
|
|
||||||
|
|||||||
@@ -193,9 +193,7 @@ impl Frame {
|
|||||||
let where_to_put_background = ui.painter().add(Shape::Noop);
|
let where_to_put_background = ui.painter().add(Shape::Noop);
|
||||||
let outer_rect_bounds = ui.available_rect_before_wrap();
|
let outer_rect_bounds = ui.available_rect_before_wrap();
|
||||||
|
|
||||||
let mut inner_rect = outer_rect_bounds;
|
let mut inner_rect = (self.inner_margin + self.outer_margin).shrink_rect(outer_rect_bounds);
|
||||||
inner_rect.min += self.outer_margin.left_top() + self.inner_margin.left_top();
|
|
||||||
inner_rect.max -= self.outer_margin.right_bottom() + self.inner_margin.right_bottom();
|
|
||||||
|
|
||||||
// Make sure we don't shrink to the negative:
|
// Make sure we don't shrink to the negative:
|
||||||
inner_rect.max.x = inner_rect.max.x.max(inner_rect.min.x);
|
inner_rect.max.x = inner_rect.max.x.max(inner_rect.min.x);
|
||||||
@@ -256,17 +254,13 @@ impl Frame {
|
|||||||
|
|
||||||
impl Prepared {
|
impl Prepared {
|
||||||
fn paint_rect(&self) -> Rect {
|
fn paint_rect(&self) -> Rect {
|
||||||
let mut rect = self.content_ui.min_rect();
|
self.frame
|
||||||
rect.min -= self.frame.inner_margin.left_top();
|
.inner_margin
|
||||||
rect.max += self.frame.inner_margin.right_bottom();
|
.expand_rect(self.content_ui.min_rect())
|
||||||
rect
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_with_margin(&self) -> Rect {
|
fn content_with_margin(&self) -> Rect {
|
||||||
let mut rect = self.content_ui.min_rect();
|
(self.frame.inner_margin + self.frame.outer_margin).expand_rect(self.content_ui.min_rect())
|
||||||
rect.min -= self.frame.inner_margin.left_top() + self.frame.outer_margin.left_top();
|
|
||||||
rect.max += self.frame.inner_margin.right_bottom() + self.frame.outer_margin.right_bottom();
|
|
||||||
rect
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn end(self, ui: &mut Ui) -> Response {
|
pub fn end(self, ui: &mut Ui) -> Response {
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
//!
|
//!
|
||||||
//! Add your [`Window`]:s after any top-level panels.
|
//! Add your [`Window`]:s after any top-level panels.
|
||||||
|
|
||||||
use std::ops::RangeInclusive;
|
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// State regarding panels.
|
/// State regarding panels.
|
||||||
@@ -99,7 +97,7 @@ pub struct SidePanel {
|
|||||||
resizable: bool,
|
resizable: bool,
|
||||||
show_separator_line: bool,
|
show_separator_line: bool,
|
||||||
default_width: f32,
|
default_width: f32,
|
||||||
width_range: RangeInclusive<f32>,
|
width_range: Rangef,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SidePanel {
|
impl SidePanel {
|
||||||
@@ -122,7 +120,7 @@ impl SidePanel {
|
|||||||
resizable: true,
|
resizable: true,
|
||||||
show_separator_line: true,
|
show_separator_line: true,
|
||||||
default_width: 200.0,
|
default_width: 200.0,
|
||||||
width_range: 96.0..=f32::INFINITY,
|
width_range: Rangef::new(96.0, f32::INFINITY),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,26 +151,29 @@ impl SidePanel {
|
|||||||
/// The initial wrapping width of the [`SidePanel`].
|
/// The initial wrapping width of the [`SidePanel`].
|
||||||
pub fn default_width(mut self, default_width: f32) -> Self {
|
pub fn default_width(mut self, default_width: f32) -> Self {
|
||||||
self.default_width = default_width;
|
self.default_width = default_width;
|
||||||
self.width_range = self.width_range.start().at_most(default_width)
|
self.width_range = Rangef::new(
|
||||||
..=self.width_range.end().at_least(default_width);
|
self.width_range.min.at_most(default_width),
|
||||||
|
self.width_range.max.at_least(default_width),
|
||||||
|
);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Minimum width of the panel.
|
/// Minimum width of the panel.
|
||||||
pub fn min_width(mut self, min_width: f32) -> Self {
|
pub fn min_width(mut self, min_width: f32) -> Self {
|
||||||
self.width_range = min_width..=self.width_range.end().at_least(min_width);
|
self.width_range = Rangef::new(min_width, self.width_range.max.at_least(min_width));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maximum width of the panel.
|
/// Maximum width of the panel.
|
||||||
pub fn max_width(mut self, max_width: f32) -> Self {
|
pub fn max_width(mut self, max_width: f32) -> Self {
|
||||||
self.width_range = self.width_range.start().at_most(max_width)..=max_width;
|
self.width_range = Rangef::new(self.width_range.min.at_most(max_width), max_width);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The allowable width range for the panel.
|
/// The allowable width range for the panel.
|
||||||
pub fn width_range(mut self, width_range: RangeInclusive<f32>) -> Self {
|
pub fn width_range(mut self, width_range: impl Into<Rangef>) -> Self {
|
||||||
self.default_width = clamp_to_range(self.default_width, width_range.clone());
|
let width_range = width_range.into();
|
||||||
|
self.default_width = clamp_to_range(self.default_width, width_range);
|
||||||
self.width_range = width_range;
|
self.width_range = width_range;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -180,7 +181,7 @@ impl SidePanel {
|
|||||||
/// Enforce this exact width.
|
/// Enforce this exact width.
|
||||||
pub fn exact_width(mut self, width: f32) -> Self {
|
pub fn exact_width(mut self, width: f32) -> Self {
|
||||||
self.default_width = width;
|
self.default_width = width;
|
||||||
self.width_range = width..=width;
|
self.width_range = Rangef::point(width);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +225,7 @@ impl SidePanel {
|
|||||||
if let Some(state) = PanelState::load(ui.ctx(), id) {
|
if let Some(state) = PanelState::load(ui.ctx(), id) {
|
||||||
width = state.rect.width();
|
width = state.rect.width();
|
||||||
}
|
}
|
||||||
width = clamp_to_range(width, width_range.clone()).at_most(available_rect.width());
|
width = clamp_to_range(width, width_range).at_most(available_rect.width());
|
||||||
side.set_rect_width(&mut panel_rect, width);
|
side.set_rect_width(&mut panel_rect, width);
|
||||||
ui.ctx().check_for_id_clash(id, panel_rect, "SidePanel");
|
ui.ctx().check_for_id_clash(id, panel_rect, "SidePanel");
|
||||||
}
|
}
|
||||||
@@ -241,7 +242,7 @@ impl SidePanel {
|
|||||||
|
|
||||||
let resize_x = side.opposite().side_x(panel_rect);
|
let resize_x = side.opposite().side_x(panel_rect);
|
||||||
let mouse_over_resize_line = we_are_on_top
|
let mouse_over_resize_line = we_are_on_top
|
||||||
&& panel_rect.y_range().contains(&pointer.y)
|
&& panel_rect.y_range().contains(pointer.y)
|
||||||
&& (resize_x - pointer.x).abs()
|
&& (resize_x - pointer.x).abs()
|
||||||
<= ui.style().interaction.resize_grab_radius_side;
|
<= ui.style().interaction.resize_grab_radius_side;
|
||||||
|
|
||||||
@@ -253,8 +254,7 @@ impl SidePanel {
|
|||||||
is_resizing = ui.memory(|mem| mem.is_being_dragged(resize_id));
|
is_resizing = ui.memory(|mem| mem.is_being_dragged(resize_id));
|
||||||
if is_resizing {
|
if is_resizing {
|
||||||
let width = (pointer.x - side.side_x(panel_rect)).abs();
|
let width = (pointer.x - side.side_x(panel_rect)).abs();
|
||||||
let width =
|
let width = clamp_to_range(width, width_range).at_most(available_rect.width());
|
||||||
clamp_to_range(width, width_range.clone()).at_most(available_rect.width());
|
|
||||||
side.set_rect_width(&mut panel_rect, width);
|
side.set_rect_width(&mut panel_rect, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +273,7 @@ impl SidePanel {
|
|||||||
let frame = frame.unwrap_or_else(|| Frame::side_top_panel(ui.style()));
|
let frame = frame.unwrap_or_else(|| Frame::side_top_panel(ui.style()));
|
||||||
let inner_response = frame.show(&mut panel_ui, |ui| {
|
let inner_response = frame.show(&mut panel_ui, |ui| {
|
||||||
ui.set_min_height(ui.max_rect().height()); // Make sure the frame fills the full height
|
ui.set_min_height(ui.max_rect().height()); // Make sure the frame fills the full height
|
||||||
ui.set_min_width(*width_range.start());
|
ui.set_min_width(width_range.min);
|
||||||
add_contents(ui)
|
add_contents(ui)
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -544,7 +544,7 @@ pub struct TopBottomPanel {
|
|||||||
resizable: bool,
|
resizable: bool,
|
||||||
show_separator_line: bool,
|
show_separator_line: bool,
|
||||||
default_height: Option<f32>,
|
default_height: Option<f32>,
|
||||||
height_range: RangeInclusive<f32>,
|
height_range: Rangef,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TopBottomPanel {
|
impl TopBottomPanel {
|
||||||
@@ -567,7 +567,7 @@ impl TopBottomPanel {
|
|||||||
resizable: false,
|
resizable: false,
|
||||||
show_separator_line: true,
|
show_separator_line: true,
|
||||||
default_height: None,
|
default_height: None,
|
||||||
height_range: 20.0..=f32::INFINITY,
|
height_range: Rangef::new(20.0, f32::INFINITY),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -599,28 +599,31 @@ impl TopBottomPanel {
|
|||||||
/// Defaults to [`style::Spacing::interact_size`].y.
|
/// Defaults to [`style::Spacing::interact_size`].y.
|
||||||
pub fn default_height(mut self, default_height: f32) -> Self {
|
pub fn default_height(mut self, default_height: f32) -> Self {
|
||||||
self.default_height = Some(default_height);
|
self.default_height = Some(default_height);
|
||||||
self.height_range = self.height_range.start().at_most(default_height)
|
self.height_range = Rangef::new(
|
||||||
..=self.height_range.end().at_least(default_height);
|
self.height_range.min.at_most(default_height),
|
||||||
|
self.height_range.max.at_least(default_height),
|
||||||
|
);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Minimum height of the panel.
|
/// Minimum height of the panel.
|
||||||
pub fn min_height(mut self, min_height: f32) -> Self {
|
pub fn min_height(mut self, min_height: f32) -> Self {
|
||||||
self.height_range = min_height..=self.height_range.end().at_least(min_height);
|
self.height_range = Rangef::new(min_height, self.height_range.max.at_least(min_height));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maximum height of the panel.
|
/// Maximum height of the panel.
|
||||||
pub fn max_height(mut self, max_height: f32) -> Self {
|
pub fn max_height(mut self, max_height: f32) -> Self {
|
||||||
self.height_range = self.height_range.start().at_most(max_height)..=max_height;
|
self.height_range = Rangef::new(self.height_range.min.at_most(max_height), max_height);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The allowable height range for the panel.
|
/// The allowable height range for the panel.
|
||||||
pub fn height_range(mut self, height_range: RangeInclusive<f32>) -> Self {
|
pub fn height_range(mut self, height_range: impl Into<Rangef>) -> Self {
|
||||||
|
let height_range = height_range.into();
|
||||||
self.default_height = self
|
self.default_height = self
|
||||||
.default_height
|
.default_height
|
||||||
.map(|default_height| clamp_to_range(default_height, height_range.clone()));
|
.map(|default_height| clamp_to_range(default_height, height_range));
|
||||||
self.height_range = height_range;
|
self.height_range = height_range;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -628,7 +631,7 @@ impl TopBottomPanel {
|
|||||||
/// Enforce this exact height.
|
/// Enforce this exact height.
|
||||||
pub fn exact_height(mut self, height: f32) -> Self {
|
pub fn exact_height(mut self, height: f32) -> Self {
|
||||||
self.default_height = Some(height);
|
self.default_height = Some(height);
|
||||||
self.height_range = height..=height;
|
self.height_range = Rangef::point(height);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,7 +676,7 @@ impl TopBottomPanel {
|
|||||||
} else {
|
} else {
|
||||||
default_height.unwrap_or_else(|| ui.style().spacing.interact_size.y)
|
default_height.unwrap_or_else(|| ui.style().spacing.interact_size.y)
|
||||||
};
|
};
|
||||||
height = clamp_to_range(height, height_range.clone()).at_most(available_rect.height());
|
height = clamp_to_range(height, height_range).at_most(available_rect.height());
|
||||||
side.set_rect_height(&mut panel_rect, height);
|
side.set_rect_height(&mut panel_rect, height);
|
||||||
ui.ctx()
|
ui.ctx()
|
||||||
.check_for_id_clash(id, panel_rect, "TopBottomPanel");
|
.check_for_id_clash(id, panel_rect, "TopBottomPanel");
|
||||||
@@ -692,7 +695,7 @@ impl TopBottomPanel {
|
|||||||
|
|
||||||
let resize_y = side.opposite().side_y(panel_rect);
|
let resize_y = side.opposite().side_y(panel_rect);
|
||||||
let mouse_over_resize_line = we_are_on_top
|
let mouse_over_resize_line = we_are_on_top
|
||||||
&& panel_rect.x_range().contains(&pointer.x)
|
&& panel_rect.x_range().contains(pointer.x)
|
||||||
&& (resize_y - pointer.y).abs()
|
&& (resize_y - pointer.y).abs()
|
||||||
<= ui.style().interaction.resize_grab_radius_side;
|
<= ui.style().interaction.resize_grab_radius_side;
|
||||||
|
|
||||||
@@ -704,8 +707,8 @@ impl TopBottomPanel {
|
|||||||
is_resizing = ui.memory(|mem| mem.interaction.drag_id == Some(resize_id));
|
is_resizing = ui.memory(|mem| mem.interaction.drag_id == Some(resize_id));
|
||||||
if is_resizing {
|
if is_resizing {
|
||||||
let height = (pointer.y - side.side_y(panel_rect)).abs();
|
let height = (pointer.y - side.side_y(panel_rect)).abs();
|
||||||
let height = clamp_to_range(height, height_range.clone())
|
let height =
|
||||||
.at_most(available_rect.height());
|
clamp_to_range(height, height_range).at_most(available_rect.height());
|
||||||
side.set_rect_height(&mut panel_rect, height);
|
side.set_rect_height(&mut panel_rect, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,7 +727,7 @@ impl TopBottomPanel {
|
|||||||
let frame = frame.unwrap_or_else(|| Frame::side_top_panel(ui.style()));
|
let frame = frame.unwrap_or_else(|| Frame::side_top_panel(ui.style()));
|
||||||
let inner_response = frame.show(&mut panel_ui, |ui| {
|
let inner_response = frame.show(&mut panel_ui, |ui| {
|
||||||
ui.set_min_width(ui.max_rect().width()); // Make the frame fill full width
|
ui.set_min_width(ui.max_rect().width()); // Make the frame fill full width
|
||||||
ui.set_min_height(*height_range.start());
|
ui.set_min_height(height_range.min);
|
||||||
add_contents(ui)
|
add_contents(ui)
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1056,9 +1059,7 @@ impl CentralPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clamp_to_range(x: f32, range: RangeInclusive<f32>) -> f32 {
|
fn clamp_to_range(x: f32, range: Rangef) -> f32 {
|
||||||
x.clamp(
|
let range = range.as_positive();
|
||||||
range.start().min(*range.end()),
|
x.clamp(range.min, range.max)
|
||||||
range.start().max(*range.end()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,7 +124,10 @@ impl Resize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Can you resize it with the mouse?
|
/// Can you resize it with the mouse?
|
||||||
/// Note that a window can still auto-resize
|
///
|
||||||
|
/// Note that a window can still auto-resize.
|
||||||
|
///
|
||||||
|
/// Default is `true`.
|
||||||
pub fn resizable(mut self, resizable: bool) -> Self {
|
pub fn resizable(mut self, resizable: bool) -> Self {
|
||||||
self.resizable = resizable;
|
self.resizable = resizable;
|
||||||
self
|
self
|
||||||
|
|||||||
@@ -334,16 +334,22 @@ struct Prepared {
|
|||||||
state: State,
|
state: State,
|
||||||
has_bar: [bool; 2],
|
has_bar: [bool; 2],
|
||||||
auto_shrink: [bool; 2],
|
auto_shrink: [bool; 2],
|
||||||
|
|
||||||
/// How much horizontal and vertical space are used up by the
|
/// How much horizontal and vertical space are used up by the
|
||||||
/// width of the vertical bar, and the height of the horizontal bar?
|
/// width of the vertical bar, and the height of the horizontal bar?
|
||||||
current_bar_use: Vec2,
|
current_bar_use: Vec2,
|
||||||
|
|
||||||
scroll_bar_visibility: ScrollBarVisibility,
|
scroll_bar_visibility: ScrollBarVisibility,
|
||||||
|
|
||||||
/// Where on the screen the content is (excludes scroll bars).
|
/// Where on the screen the content is (excludes scroll bars).
|
||||||
inner_rect: Rect,
|
inner_rect: Rect,
|
||||||
|
|
||||||
content_ui: Ui,
|
content_ui: Ui,
|
||||||
|
|
||||||
/// Relative coordinates: the offset and size of the view of the inner UI.
|
/// Relative coordinates: the offset and size of the view of the inner UI.
|
||||||
/// `viewport.min == ZERO` means we scrolled to the top.
|
/// `viewport.min == ZERO` means we scrolled to the top.
|
||||||
viewport: Rect,
|
viewport: Rect,
|
||||||
|
|
||||||
scrolling_enabled: bool,
|
scrolling_enabled: bool,
|
||||||
stick_to_end: [bool; 2],
|
stick_to_end: [bool; 2],
|
||||||
}
|
}
|
||||||
@@ -459,7 +465,7 @@ impl ScrollArea {
|
|||||||
content_clip_rect.max[d] = ui.clip_rect().max[d] - current_bar_use[d];
|
content_clip_rect.max[d] = ui.clip_rect().max[d] - current_bar_use[d];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Make sure we din't accidentally expand the clip rect
|
// Make sure we didn't accidentally expand the clip rect
|
||||||
content_clip_rect = content_clip_rect.intersect(ui.clip_rect());
|
content_clip_rect = content_clip_rect.intersect(ui.clip_rect());
|
||||||
content_ui.set_clip_rect(content_clip_rect);
|
content_ui.set_clip_rect(content_clip_rect);
|
||||||
}
|
}
|
||||||
@@ -640,8 +646,7 @@ impl Prepared {
|
|||||||
let min = content_ui.min_rect().min[d];
|
let min = content_ui.min_rect().min[d];
|
||||||
let clip_rect = content_ui.clip_rect();
|
let clip_rect = content_ui.clip_rect();
|
||||||
let visible_range = min..=min + clip_rect.size()[d];
|
let visible_range = min..=min + clip_rect.size()[d];
|
||||||
let start = *scroll.start();
|
let (start, end) = (scroll.min, scroll.max);
|
||||||
let end = *scroll.end();
|
|
||||||
let clip_start = clip_rect.min[d];
|
let clip_start = clip_rect.min[d];
|
||||||
let clip_end = clip_rect.max[d];
|
let clip_end = clip_rect.max[d];
|
||||||
let mut spacing = ui.spacing().item_spacing[d];
|
let mut spacing = ui.spacing().item_spacing[d];
|
||||||
|
|||||||
@@ -217,7 +217,10 @@ impl<'open> Window<'open> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Can the user resize the window by dragging its edges?
|
/// Can the user resize the window by dragging its edges?
|
||||||
|
///
|
||||||
/// Note that even if you set this to `false` the window may still auto-resize.
|
/// Note that even if you set this to `false` the window may still auto-resize.
|
||||||
|
///
|
||||||
|
/// Default is `true`.
|
||||||
pub fn resizable(mut self, resizable: bool) -> Self {
|
pub fn resizable(mut self, resizable: bool) -> Self {
|
||||||
self.resize = self.resize.resizable(resizable);
|
self.resize = self.resize.resizable(resizable);
|
||||||
self
|
self
|
||||||
|
|||||||
@@ -566,7 +566,7 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let show_error = |widget_rect: Rect, text: String| {
|
let show_error = |widget_rect: Rect, text: String| {
|
||||||
let text = format!("🔥 {}", text);
|
let text = format!("🔥 {text}");
|
||||||
let color = self.style().visuals.error_fg_color;
|
let color = self.style().visuals.error_fg_color;
|
||||||
let painter = self.debug_painter();
|
let painter = self.debug_painter();
|
||||||
painter.rect_stroke(widget_rect, 0.0, (1.0, color));
|
painter.rect_stroke(widget_rect, 0.0, (1.0, color));
|
||||||
@@ -612,10 +612,10 @@ impl Context {
|
|||||||
let id_str = id.short_debug_format();
|
let id_str = id.short_debug_format();
|
||||||
|
|
||||||
if prev_rect.min.distance(new_rect.min) < 4.0 {
|
if prev_rect.min.distance(new_rect.min) < 4.0 {
|
||||||
show_error(new_rect, format!("Double use of {} ID {}", what, id_str));
|
show_error(new_rect, format!("Double use of {what} ID {id_str}"));
|
||||||
} else {
|
} else {
|
||||||
show_error(prev_rect, format!("First use of {} ID {}", what, id_str));
|
show_error(prev_rect, format!("First use of {what} ID {id_str}"));
|
||||||
show_error(new_rect, format!("Second use of {} ID {}", what, id_str));
|
show_error(new_rect, format!("Second use of {what} ID {id_str}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1574,14 +1574,14 @@ impl Context {
|
|||||||
|
|
||||||
let pointer_pos = self
|
let pointer_pos = self
|
||||||
.pointer_hover_pos()
|
.pointer_hover_pos()
|
||||||
.map_or_else(String::new, |pos| format!("{:?}", pos));
|
.map_or_else(String::new, |pos| format!("{pos:?}"));
|
||||||
ui.label(format!("Pointer pos: {}", pointer_pos));
|
ui.label(format!("Pointer pos: {pointer_pos}"));
|
||||||
|
|
||||||
let top_layer = self
|
let top_layer = self
|
||||||
.pointer_hover_pos()
|
.pointer_hover_pos()
|
||||||
.and_then(|pos| self.layer_id_at(pos))
|
.and_then(|pos| self.layer_id_at(pos))
|
||||||
.map_or_else(String::new, |layer| layer.short_debug_format());
|
.map_or_else(String::new, |layer| layer.short_debug_format());
|
||||||
ui.label(format!("Top layer under mouse: {}", top_layer));
|
ui.label(format!("Top layer under mouse: {top_layer}"));
|
||||||
|
|
||||||
ui.add_space(16.0);
|
ui.add_space(16.0);
|
||||||
|
|
||||||
@@ -1667,7 +1667,7 @@ impl Context {
|
|||||||
ui.image(texture_id, size);
|
ui.image(texture_id, size);
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.label(format!("{} x {}", w, h));
|
ui.label(format!("{w} x {h}"));
|
||||||
ui.label(format!("{:.3} MB", meta.bytes_used() as f64 * 1e-6));
|
ui.label(format!("{:.3} MB", meta.bytes_used() as f64 * 1e-6));
|
||||||
ui.label(format!("{:?}", meta.name));
|
ui.label(format!("{:?}", meta.name));
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
@@ -1688,8 +1688,7 @@ impl Context {
|
|||||||
|
|
||||||
let (num_state, num_serialized) = self.data(|d| (d.len(), d.count_serialized()));
|
let (num_state, num_serialized) = self.data(|d| (d.len(), d.count_serialized()));
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"{} widget states stored (of which {} are serialized).",
|
"{num_state} widget states stored (of which {num_serialized} are serialized)."
|
||||||
num_state, num_serialized
|
|
||||||
));
|
));
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
|
|||||||
@@ -610,11 +610,11 @@ pub struct ModifierNames<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ModifierNames<'static> {
|
impl ModifierNames<'static> {
|
||||||
/// ⌥ ^ ⇧ ⌘ - NOTE: not supported by the default egui font.
|
/// ⌥ ⌃ ⇧ ⌘ - NOTE: not supported by the default egui font.
|
||||||
pub const SYMBOLS: Self = Self {
|
pub const SYMBOLS: Self = Self {
|
||||||
is_short: true,
|
is_short: true,
|
||||||
alt: "⌥",
|
alt: "⌥",
|
||||||
ctrl: "^",
|
ctrl: "⌃",
|
||||||
shift: "⇧",
|
shift: "⇧",
|
||||||
mac_cmd: "⌘",
|
mac_cmd: "⌘",
|
||||||
mac_alt: "⌥",
|
mac_alt: "⌥",
|
||||||
@@ -693,27 +693,37 @@ pub enum Key {
|
|||||||
|
|
||||||
/// The virtual keycode for the Minus key.
|
/// The virtual keycode for the Minus key.
|
||||||
Minus,
|
Minus,
|
||||||
|
|
||||||
/// The virtual keycode for the Plus/Equals key.
|
/// The virtual keycode for the Plus/Equals key.
|
||||||
PlusEquals,
|
PlusEquals,
|
||||||
|
|
||||||
/// Either from the main row or from the numpad.
|
/// Either from the main row or from the numpad.
|
||||||
Num0,
|
Num0,
|
||||||
|
|
||||||
/// Either from the main row or from the numpad.
|
/// Either from the main row or from the numpad.
|
||||||
Num1,
|
Num1,
|
||||||
|
|
||||||
/// Either from the main row or from the numpad.
|
/// Either from the main row or from the numpad.
|
||||||
Num2,
|
Num2,
|
||||||
|
|
||||||
/// Either from the main row or from the numpad.
|
/// Either from the main row or from the numpad.
|
||||||
Num3,
|
Num3,
|
||||||
|
|
||||||
/// Either from the main row or from the numpad.
|
/// Either from the main row or from the numpad.
|
||||||
Num4,
|
Num4,
|
||||||
|
|
||||||
/// Either from the main row or from the numpad.
|
/// Either from the main row or from the numpad.
|
||||||
Num5,
|
Num5,
|
||||||
|
|
||||||
/// Either from the main row or from the numpad.
|
/// Either from the main row or from the numpad.
|
||||||
Num6,
|
Num6,
|
||||||
|
|
||||||
/// Either from the main row or from the numpad.
|
/// Either from the main row or from the numpad.
|
||||||
Num7,
|
Num7,
|
||||||
|
|
||||||
/// Either from the main row or from the numpad.
|
/// Either from the main row or from the numpad.
|
||||||
Num8,
|
Num8,
|
||||||
|
|
||||||
/// Either from the main row or from the numpad.
|
/// Either from the main row or from the numpad.
|
||||||
Num9,
|
Num9,
|
||||||
|
|
||||||
@@ -906,7 +916,7 @@ fn format_kb_shortcut() {
|
|||||||
cmd_shift_f.format(&ModifierNames::NAMES, true),
|
cmd_shift_f.format(&ModifierNames::NAMES, true),
|
||||||
"Shift+Cmd+F"
|
"Shift+Cmd+F"
|
||||||
);
|
);
|
||||||
assert_eq!(cmd_shift_f.format(&ModifierNames::SYMBOLS, false), "^⇧F");
|
assert_eq!(cmd_shift_f.format(&ModifierNames::SYMBOLS, false), "⌃⇧F");
|
||||||
assert_eq!(cmd_shift_f.format(&ModifierNames::SYMBOLS, true), "⇧⌘F");
|
assert_eq!(cmd_shift_f.format(&ModifierNames::SYMBOLS, true), "⇧⌘F");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -927,25 +937,25 @@ impl RawInput {
|
|||||||
focused,
|
focused,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
ui.label(format!("screen_rect: {:?} points", screen_rect));
|
ui.label(format!("screen_rect: {screen_rect:?} points"));
|
||||||
ui.label(format!("pixels_per_point: {:?}", pixels_per_point))
|
ui.label(format!("pixels_per_point: {pixels_per_point:?}"))
|
||||||
.on_hover_text(
|
.on_hover_text(
|
||||||
"Also called HDPI factor.\nNumber of physical pixels per each logical pixel.",
|
"Also called HDPI factor.\nNumber of physical pixels per each logical pixel.",
|
||||||
);
|
);
|
||||||
ui.label(format!("max_texture_side: {:?}", max_texture_side));
|
ui.label(format!("max_texture_side: {max_texture_side:?}"));
|
||||||
if let Some(time) = time {
|
if let Some(time) = time {
|
||||||
ui.label(format!("time: {:.3} s", time));
|
ui.label(format!("time: {time:.3} s"));
|
||||||
} else {
|
} else {
|
||||||
ui.label("time: None");
|
ui.label("time: None");
|
||||||
}
|
}
|
||||||
ui.label(format!("predicted_dt: {:.1} ms", 1e3 * predicted_dt));
|
ui.label(format!("predicted_dt: {:.1} ms", 1e3 * predicted_dt));
|
||||||
ui.label(format!("modifiers: {:#?}", modifiers));
|
ui.label(format!("modifiers: {modifiers:#?}"));
|
||||||
ui.label(format!("hovered_files: {}", hovered_files.len()));
|
ui.label(format!("hovered_files: {}", hovered_files.len()));
|
||||||
ui.label(format!("dropped_files: {}", dropped_files.len()));
|
ui.label(format!("dropped_files: {}", dropped_files.len()));
|
||||||
ui.label(format!("focused: {}", focused));
|
ui.label(format!("focused: {focused}"));
|
||||||
ui.scope(|ui| {
|
ui.scope(|ui| {
|
||||||
ui.set_min_height(150.0);
|
ui.set_min_height(150.0);
|
||||||
ui.label(format!("events: {:#?}", events))
|
ui.label(format!("events: {events:#?}"))
|
||||||
.on_hover_text("key presses etc");
|
.on_hover_text("key presses etc");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ impl PlatformOutput {
|
|||||||
/// This can be used by a text-to-speech system to describe the events (if any).
|
/// This can be used by a text-to-speech system to describe the events (if any).
|
||||||
pub fn events_description(&self) -> String {
|
pub fn events_description(&self) -> String {
|
||||||
// only describe last event:
|
// only describe last event:
|
||||||
if let Some(event) = self.events.iter().rev().next() {
|
if let Some(event) = self.events.iter().next_back() {
|
||||||
match event {
|
match event {
|
||||||
OutputEvent::Clicked(widget_info)
|
OutputEvent::Clicked(widget_info)
|
||||||
| OutputEvent::DoubleClicked(widget_info)
|
| OutputEvent::DoubleClicked(widget_info)
|
||||||
@@ -417,12 +417,12 @@ impl OutputEvent {
|
|||||||
impl std::fmt::Debug for OutputEvent {
|
impl std::fmt::Debug for OutputEvent {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Clicked(wi) => write!(f, "Clicked({:?})", wi),
|
Self::Clicked(wi) => write!(f, "Clicked({wi:?})"),
|
||||||
Self::DoubleClicked(wi) => write!(f, "DoubleClicked({:?})", wi),
|
Self::DoubleClicked(wi) => write!(f, "DoubleClicked({wi:?})"),
|
||||||
Self::TripleClicked(wi) => write!(f, "TripleClicked({:?})", wi),
|
Self::TripleClicked(wi) => write!(f, "TripleClicked({wi:?})"),
|
||||||
Self::FocusGained(wi) => write!(f, "FocusGained({:?})", wi),
|
Self::FocusGained(wi) => write!(f, "FocusGained({wi:?})"),
|
||||||
Self::TextSelectionChanged(wi) => write!(f, "TextSelectionChanged({:?})", wi),
|
Self::TextSelectionChanged(wi) => write!(f, "TextSelectionChanged({wi:?})"),
|
||||||
Self::ValueChanged(wi) => write!(f, "ValueChanged({:?})", wi),
|
Self::ValueChanged(wi) => write!(f, "ValueChanged({wi:?})"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -609,14 +609,14 @@ impl WidgetInfo {
|
|||||||
if let Some(selected) = selected {
|
if let Some(selected) = selected {
|
||||||
if *typ == WidgetType::Checkbox {
|
if *typ == WidgetType::Checkbox {
|
||||||
let state = if *selected { "checked" } else { "unchecked" };
|
let state = if *selected { "checked" } else { "unchecked" };
|
||||||
description = format!("{} {}", state, description);
|
description = format!("{state} {description}");
|
||||||
} else {
|
} else {
|
||||||
description += if *selected { "selected" } else { "" };
|
description += if *selected { "selected" } else { "" };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(label) = label {
|
if let Some(label) = label {
|
||||||
description = format!("{}: {}", label, description);
|
description = format!("{label}: {description}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ == &WidgetType::TextEdit {
|
if typ == &WidgetType::TextEdit {
|
||||||
@@ -630,7 +630,7 @@ impl WidgetInfo {
|
|||||||
} else {
|
} else {
|
||||||
text = "blank".into();
|
text = "blank".into();
|
||||||
}
|
}
|
||||||
description = format!("{}: {}", text, description);
|
description = format!("{text}: {description}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(value) = value {
|
if let Some(value) = value {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use std::ops::RangeInclusive;
|
|
||||||
|
|
||||||
use crate::{id::IdSet, *};
|
use crate::{id::IdSet, *};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
@@ -46,7 +44,7 @@ pub(crate) struct FrameState {
|
|||||||
pub(crate) scroll_delta: Vec2, // TODO(emilk): move to `InputState` ?
|
pub(crate) scroll_delta: Vec2, // TODO(emilk): move to `InputState` ?
|
||||||
|
|
||||||
/// horizontal, vertical
|
/// horizontal, vertical
|
||||||
pub(crate) scroll_target: [Option<(RangeInclusive<f32>, Option<Align>)>; 2],
|
pub(crate) scroll_target: [Option<(Rangef, Option<Align>)>; 2],
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
pub(crate) accesskit_state: Option<AccessKitFrameState>,
|
pub(crate) accesskit_state: Option<AccessKitFrameState>,
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ impl State {
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// type alias for boxed function to determine row color during grid generation
|
// type alias for boxed function to determine row color during grid generation
|
||||||
type ColorPickerFn = Box<dyn Fn(usize, &Style) -> Option<Color32>>;
|
type ColorPickerFn = Box<dyn Send + Sync + Fn(usize, &Style) -> Option<Color32>>;
|
||||||
|
|
||||||
pub(crate) struct GridLayout {
|
pub(crate) struct GridLayout {
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
@@ -60,6 +60,7 @@ pub(crate) struct GridLayout {
|
|||||||
/// State previous frame (if any).
|
/// State previous frame (if any).
|
||||||
/// This can be used to predict future sizes of cells.
|
/// This can be used to predict future sizes of cells.
|
||||||
prev_state: State,
|
prev_state: State,
|
||||||
|
|
||||||
/// State accumulated during the current frame.
|
/// State accumulated during the current frame.
|
||||||
curr_state: State,
|
curr_state: State,
|
||||||
initial_available: Rect,
|
initial_available: Rect,
|
||||||
@@ -311,7 +312,7 @@ impl Grid {
|
|||||||
/// Setting this will allow for dynamic coloring of rows of the grid object
|
/// Setting this will allow for dynamic coloring of rows of the grid object
|
||||||
pub fn with_row_color<F>(mut self, color_picker: F) -> Self
|
pub fn with_row_color<F>(mut self, color_picker: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(usize, &Style) -> Option<Color32> + 'static,
|
F: Send + Sync + Fn(usize, &Style) -> Option<Color32> + 'static,
|
||||||
{
|
{
|
||||||
self.color_picker = Some(Box::new(color_picker));
|
self.color_picker = Some(Box::new(color_picker));
|
||||||
self
|
self
|
||||||
|
|||||||
@@ -990,30 +990,28 @@ impl InputState {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.label(format!("scroll_delta: {:?} points", scroll_delta));
|
ui.label(format!("scroll_delta: {scroll_delta:?} points"));
|
||||||
ui.label(format!("zoom_factor_delta: {:4.2}x", zoom_factor_delta));
|
ui.label(format!("zoom_factor_delta: {zoom_factor_delta:4.2}x"));
|
||||||
ui.label(format!("screen_rect: {:?} points", screen_rect));
|
ui.label(format!("screen_rect: {screen_rect:?} points"));
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"{} physical pixels for each logical point",
|
"{pixels_per_point} physical pixels for each logical point"
|
||||||
pixels_per_point
|
|
||||||
));
|
));
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"max texture size (on each side): {}",
|
"max texture size (on each side): {max_texture_side}"
|
||||||
max_texture_side
|
|
||||||
));
|
));
|
||||||
ui.label(format!("time: {:.3} s", time));
|
ui.label(format!("time: {time:.3} s"));
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"time since previous frame: {:.1} ms",
|
"time since previous frame: {:.1} ms",
|
||||||
1e3 * unstable_dt
|
1e3 * unstable_dt
|
||||||
));
|
));
|
||||||
ui.label(format!("predicted_dt: {:.1} ms", 1e3 * predicted_dt));
|
ui.label(format!("predicted_dt: {:.1} ms", 1e3 * predicted_dt));
|
||||||
ui.label(format!("stable_dt: {:.1} ms", 1e3 * stable_dt));
|
ui.label(format!("stable_dt: {:.1} ms", 1e3 * stable_dt));
|
||||||
ui.label(format!("focused: {}", focused));
|
ui.label(format!("focused: {focused}"));
|
||||||
ui.label(format!("modifiers: {:#?}", modifiers));
|
ui.label(format!("modifiers: {modifiers:#?}"));
|
||||||
ui.label(format!("keys_down: {:?}", keys_down));
|
ui.label(format!("keys_down: {keys_down:?}"));
|
||||||
ui.scope(|ui| {
|
ui.scope(|ui| {
|
||||||
ui.set_min_height(150.0);
|
ui.set_min_height(150.0);
|
||||||
ui.label(format!("events: {:#?}", events))
|
ui.label(format!("events: {events:#?}"))
|
||||||
.on_hover_text("key presses etc");
|
.on_hover_text("key presses etc");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1037,22 +1035,21 @@ impl PointerState {
|
|||||||
pointer_events,
|
pointer_events,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
ui.label(format!("latest_pos: {:?}", latest_pos));
|
ui.label(format!("latest_pos: {latest_pos:?}"));
|
||||||
ui.label(format!("interact_pos: {:?}", interact_pos));
|
ui.label(format!("interact_pos: {interact_pos:?}"));
|
||||||
ui.label(format!("delta: {:?}", delta));
|
ui.label(format!("delta: {delta:?}"));
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"velocity: [{:3.0} {:3.0}] points/sec",
|
"velocity: [{:3.0} {:3.0}] points/sec",
|
||||||
velocity.x, velocity.y
|
velocity.x, velocity.y
|
||||||
));
|
));
|
||||||
ui.label(format!("down: {:#?}", down));
|
ui.label(format!("down: {down:#?}"));
|
||||||
ui.label(format!("press_origin: {:?}", press_origin));
|
ui.label(format!("press_origin: {press_origin:?}"));
|
||||||
ui.label(format!("press_start_time: {:?} s", press_start_time));
|
ui.label(format!("press_start_time: {press_start_time:?} s"));
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"has_moved_too_much_for_a_click: {}",
|
"has_moved_too_much_for_a_click: {has_moved_too_much_for_a_click}"
|
||||||
has_moved_too_much_for_a_click
|
|
||||||
));
|
));
|
||||||
ui.label(format!("last_click_time: {:#?}", last_click_time));
|
ui.label(format!("last_click_time: {last_click_time:#?}"));
|
||||||
ui.label(format!("last_last_click_time: {:#?}", last_last_click_time));
|
ui.label(format!("last_last_click_time: {last_last_click_time:#?}"));
|
||||||
ui.label(format!("pointer_events: {:?}", pointer_events));
|
ui.label(format!("pointer_events: {pointer_events:?}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ impl TouchState {
|
|||||||
|
|
||||||
impl TouchState {
|
impl TouchState {
|
||||||
pub fn ui(&self, ui: &mut crate::Ui) {
|
pub fn ui(&self, ui: &mut crate::Ui) {
|
||||||
ui.label(format!("{:?}", self));
|
ui.label(format!("{self:?}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +294,7 @@ impl Debug for TouchState {
|
|||||||
// This outputs less clutter than `#[derive(Debug)]`:
|
// This outputs less clutter than `#[derive(Debug)]`:
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
for (id, touch) in &self.active_touches {
|
for (id, touch) in &self.active_touches {
|
||||||
f.write_fmt(format_args!("#{:?}: {:#?}\n", id, touch))?;
|
f.write_fmt(format_args!("#{id:?}: {touch:#?}\n"))?;
|
||||||
}
|
}
|
||||||
f.write_fmt(format_args!("gesture: {:#?}\n", self.gesture_state))?;
|
f.write_fmt(format_args!("gesture: {:#?}\n", self.gesture_state))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -31,10 +31,7 @@ pub(crate) fn font_texture_ui(ui: &mut Ui, [width, height]: [usize; 2]) -> Respo
|
|||||||
Color32::BLACK
|
Color32::BLACK
|
||||||
};
|
};
|
||||||
|
|
||||||
ui.label(format!(
|
ui.label(format!("Texture size: {width} x {height} (hover to zoom)"));
|
||||||
"Texture size: {} x {} (hover to zoom)",
|
|
||||||
width, height
|
|
||||||
));
|
|
||||||
if width <= 1 || height <= 1 {
|
if width <= 1 || height <= 1 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -108,7 +105,7 @@ impl Widget for &epaint::stats::PaintStats {
|
|||||||
label(ui, shape_path, "paths");
|
label(ui, shape_path, "paths");
|
||||||
label(ui, shape_mesh, "nested meshes");
|
label(ui, shape_mesh, "nested meshes");
|
||||||
label(ui, shape_vec, "nested shapes");
|
label(ui, shape_vec, "nested shapes");
|
||||||
ui.label(format!("{:6} callbacks", num_callbacks));
|
ui.label(format!("{num_callbacks:6} callbacks"));
|
||||||
ui.add_space(10.0);
|
ui.add_space(10.0);
|
||||||
|
|
||||||
ui.label("Text shapes:");
|
ui.label("Text shapes:");
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ impl PaintList {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add(&mut self, clip_rect: Rect, shape: Shape) -> ShapeIdx {
|
pub fn add(&mut self, clip_rect: Rect, shape: Shape) -> ShapeIdx {
|
||||||
let idx = ShapeIdx(self.0.len());
|
let idx = ShapeIdx(self.0.len());
|
||||||
self.0.push(ClippedShape(clip_rect, shape));
|
self.0.push(ClippedShape { clip_rect, shape });
|
||||||
idx
|
idx
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ impl PaintList {
|
|||||||
self.0.extend(
|
self.0.extend(
|
||||||
shapes
|
shapes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|shape| ClippedShape(clip_rect, shape)),
|
.map(|shape| ClippedShape { clip_rect, shape }),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,12 +148,12 @@ impl PaintList {
|
|||||||
/// and then later setting it using `paint_list.set(idx, cr, frame);`.
|
/// and then later setting it using `paint_list.set(idx, cr, frame);`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set(&mut self, idx: ShapeIdx, clip_rect: Rect, shape: Shape) {
|
pub fn set(&mut self, idx: ShapeIdx, clip_rect: Rect, shape: Shape) {
|
||||||
self.0[idx.0] = ClippedShape(clip_rect, shape);
|
self.0[idx.0] = ClippedShape { clip_rect, shape };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translate each [`Shape`] and clip rectangle by this much, in-place
|
/// Translate each [`Shape`] and clip rectangle by this much, in-place
|
||||||
pub fn translate(&mut self, delta: Vec2) {
|
pub fn translate(&mut self, delta: Vec2) {
|
||||||
for ClippedShape(clip_rect, shape) in &mut self.0 {
|
for ClippedShape { clip_rect, shape } in &mut self.0 {
|
||||||
*clip_rect = clip_rect.translate(delta);
|
*clip_rect = clip_rect.translate(delta);
|
||||||
shape.translate(delta);
|
shape.translate(delta);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -335,7 +335,9 @@ pub use epaint::emath;
|
|||||||
#[cfg(feature = "color-hex")]
|
#[cfg(feature = "color-hex")]
|
||||||
pub use ecolor::hex_color;
|
pub use ecolor::hex_color;
|
||||||
pub use ecolor::{Color32, Rgba};
|
pub use ecolor::{Color32, Rgba};
|
||||||
pub use emath::{lerp, pos2, remap, remap_clamp, vec2, Align, Align2, NumExt, Pos2, Rect, Vec2};
|
pub use emath::{
|
||||||
|
lerp, pos2, remap, remap_clamp, vec2, Align, Align2, NumExt, Pos2, Rangef, Rect, Vec2,
|
||||||
|
};
|
||||||
pub use epaint::{
|
pub use epaint::{
|
||||||
mutex,
|
mutex,
|
||||||
text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak},
|
text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak},
|
||||||
|
|||||||
@@ -546,8 +546,10 @@ impl Memory {
|
|||||||
#[cfg_attr(feature = "serde", serde(default))]
|
#[cfg_attr(feature = "serde", serde(default))]
|
||||||
pub struct Areas {
|
pub struct Areas {
|
||||||
areas: IdMap<area::State>,
|
areas: IdMap<area::State>,
|
||||||
|
|
||||||
/// Back-to-front. Top is last.
|
/// Back-to-front. Top is last.
|
||||||
order: Vec<LayerId>,
|
order: Vec<LayerId>,
|
||||||
|
|
||||||
visible_last_frame: ahash::HashSet<LayerId>,
|
visible_last_frame: ahash::HashSet<LayerId>,
|
||||||
visible_current_frame: ahash::HashSet<LayerId>,
|
visible_current_frame: ahash::HashSet<LayerId>,
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use std::ops::RangeInclusive;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
emath::{Align2, Pos2, Rect, Vec2},
|
emath::{Align2, Pos2, Rangef, Rect, Vec2},
|
||||||
layers::{LayerId, PaintList, ShapeIdx},
|
layers::{LayerId, PaintList, ShapeIdx},
|
||||||
Color32, Context, FontId,
|
Color32, Context, FontId,
|
||||||
};
|
};
|
||||||
@@ -227,7 +226,7 @@ impl Painter {
|
|||||||
|
|
||||||
pub fn error(&self, pos: Pos2, text: impl std::fmt::Display) -> Rect {
|
pub fn error(&self, pos: Pos2, text: impl std::fmt::Display) -> Rect {
|
||||||
let color = self.ctx.style().visuals.error_fg_color;
|
let color = self.ctx.style().visuals.error_fg_color;
|
||||||
self.debug_text(pos, Align2::LEFT_TOP, color, format!("🔥 {}", text))
|
self.debug_text(pos, Align2::LEFT_TOP, color, format!("🔥 {text}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// text with a background
|
/// text with a background
|
||||||
@@ -263,12 +262,12 @@ impl Painter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Paints a horizontal line.
|
/// Paints a horizontal line.
|
||||||
pub fn hline(&self, x: RangeInclusive<f32>, y: f32, stroke: impl Into<Stroke>) {
|
pub fn hline(&self, x: impl Into<Rangef>, y: f32, stroke: impl Into<Stroke>) {
|
||||||
self.add(Shape::hline(x, y, stroke));
|
self.add(Shape::hline(x, y, stroke));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Paints a vertical line.
|
/// Paints a vertical line.
|
||||||
pub fn vline(&self, x: f32, y: RangeInclusive<f32>, stroke: impl Into<Stroke>) {
|
pub fn vline(&self, x: f32, y: impl Into<Rangef>, stroke: impl Into<Stroke>) {
|
||||||
self.add(Shape::vline(x, y, stroke));
|
self.add(Shape::vline(x, y, stroke));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -360,30 +360,46 @@ impl Margin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Total margins on both sides
|
/// Total margins on both sides
|
||||||
|
#[inline]
|
||||||
pub fn sum(&self) -> Vec2 {
|
pub fn sum(&self) -> Vec2 {
|
||||||
vec2(self.left + self.right, self.top + self.bottom)
|
vec2(self.left + self.right, self.top + self.bottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn left_top(&self) -> Vec2 {
|
pub fn left_top(&self) -> Vec2 {
|
||||||
vec2(self.left, self.top)
|
vec2(self.left, self.top)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn right_bottom(&self) -> Vec2 {
|
pub fn right_bottom(&self) -> Vec2 {
|
||||||
vec2(self.right, self.bottom)
|
vec2(self.right, self.bottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn is_same(&self) -> bool {
|
pub fn is_same(&self) -> bool {
|
||||||
self.left == self.right && self.left == self.top && self.left == self.bottom
|
self.left == self.right && self.left == self.top && self.left == self.bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn expand_rect(&self, rect: Rect) -> Rect {
|
||||||
|
Rect::from_min_max(rect.min - self.left_top(), rect.max + self.right_bottom())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn shrink_rect(&self, rect: Rect) -> Rect {
|
||||||
|
Rect::from_min_max(rect.min + self.left_top(), rect.max - self.right_bottom())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<f32> for Margin {
|
impl From<f32> for Margin {
|
||||||
|
#[inline]
|
||||||
fn from(v: f32) -> Self {
|
fn from(v: f32) -> Self {
|
||||||
Self::same(v)
|
Self::same(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec2> for Margin {
|
impl From<Vec2> for Margin {
|
||||||
|
#[inline]
|
||||||
fn from(v: Vec2) -> Self {
|
fn from(v: Vec2) -> Self {
|
||||||
Self::symmetric(v.x, v.y)
|
Self::symmetric(v.x, v.y)
|
||||||
}
|
}
|
||||||
@@ -392,6 +408,7 @@ impl From<Vec2> for Margin {
|
|||||||
impl std::ops::Add for Margin {
|
impl std::ops::Add for Margin {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn add(self, other: Self) -> Self {
|
fn add(self, other: Self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
left: self.left + other.left,
|
left: self.left + other.left,
|
||||||
@@ -491,7 +508,8 @@ pub struct Visuals {
|
|||||||
|
|
||||||
pub resize_corner_size: f32,
|
pub resize_corner_size: f32,
|
||||||
|
|
||||||
pub text_cursor_width: f32,
|
/// The color and width of the text cursor
|
||||||
|
pub text_cursor: Stroke,
|
||||||
|
|
||||||
/// show where the text cursor would be if you clicked
|
/// show where the text cursor would be if you clicked
|
||||||
pub text_cursor_preview: bool,
|
pub text_cursor_preview: bool,
|
||||||
@@ -767,7 +785,7 @@ impl Visuals {
|
|||||||
|
|
||||||
popup_shadow: Shadow::small_dark(),
|
popup_shadow: Shadow::small_dark(),
|
||||||
resize_corner_size: 12.0,
|
resize_corner_size: 12.0,
|
||||||
text_cursor_width: 2.0,
|
text_cursor: Stroke::new(2.0, Color32::from_rgb(192, 222, 255)),
|
||||||
text_cursor_preview: false,
|
text_cursor_preview: false,
|
||||||
clip_rect_margin: 3.0, // should be at least half the size of the widest frame stroke + max WidgetVisuals::expansion
|
clip_rect_margin: 3.0, // should be at least half the size of the widest frame stroke + max WidgetVisuals::expansion
|
||||||
button_frame: true,
|
button_frame: true,
|
||||||
@@ -800,6 +818,7 @@ impl Visuals {
|
|||||||
panel_fill: Color32::from_gray(248),
|
panel_fill: Color32::from_gray(248),
|
||||||
|
|
||||||
popup_shadow: Shadow::small_light(),
|
popup_shadow: Shadow::small_light(),
|
||||||
|
text_cursor: Stroke::new(2.0, Color32::from_rgb(0, 83, 125)),
|
||||||
..Self::dark()
|
..Self::dark()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1334,7 +1353,7 @@ impl Visuals {
|
|||||||
popup_shadow,
|
popup_shadow,
|
||||||
|
|
||||||
resize_corner_size,
|
resize_corner_size,
|
||||||
text_cursor_width,
|
text_cursor,
|
||||||
text_cursor_preview,
|
text_cursor_preview,
|
||||||
clip_rect_margin,
|
clip_rect_margin,
|
||||||
button_frame,
|
button_frame,
|
||||||
@@ -1392,8 +1411,9 @@ impl Visuals {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ui_color(ui, hyperlink_color, "hyperlink_color");
|
ui_color(ui, hyperlink_color, "hyperlink_color");
|
||||||
|
stroke_ui(ui, text_cursor, "Text Cursor");
|
||||||
|
|
||||||
ui.add(Slider::new(resize_corner_size, 0.0..=20.0).text("resize_corner_size"));
|
ui.add(Slider::new(resize_corner_size, 0.0..=20.0).text("resize_corner_size"));
|
||||||
ui.add(Slider::new(text_cursor_width, 0.0..=4.0).text("text_cursor_width"));
|
|
||||||
ui.checkbox(text_cursor_preview, "Preview text cursor on hover");
|
ui.checkbox(text_cursor_preview, "Preview text cursor on hover");
|
||||||
ui.add(Slider::new(clip_rect_margin, 0.0..=20.0).text("clip_rect_margin"));
|
ui.add(Slider::new(clip_rect_margin, 0.0..=20.0).text("clip_rect_margin"));
|
||||||
|
|
||||||
|
|||||||
@@ -517,15 +517,17 @@ impl Ui {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// `ui.set_width_range(min..=max);` is equivalent to `ui.set_min_width(min); ui.set_max_width(max);`.
|
/// `ui.set_width_range(min..=max);` is equivalent to `ui.set_min_width(min); ui.set_max_width(max);`.
|
||||||
pub fn set_width_range(&mut self, width: std::ops::RangeInclusive<f32>) {
|
pub fn set_width_range(&mut self, width: impl Into<Rangef>) {
|
||||||
self.set_min_width(*width.start());
|
let width = width.into();
|
||||||
self.set_max_width(*width.end());
|
self.set_min_width(width.min);
|
||||||
|
self.set_max_width(width.max);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `ui.set_height_range(min..=max);` is equivalent to `ui.set_min_height(min); ui.set_max_height(max);`.
|
/// `ui.set_height_range(min..=max);` is equivalent to `ui.set_min_height(min); ui.set_max_height(max);`.
|
||||||
pub fn set_height_range(&mut self, height: std::ops::RangeInclusive<f32>) {
|
pub fn set_height_range(&mut self, height: impl Into<Rangef>) {
|
||||||
self.set_min_height(*height.start());
|
let height = height.into();
|
||||||
self.set_max_height(*height.end());
|
self.set_min_height(height.min);
|
||||||
|
self.set_max_height(height.max);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set both the minimum and maximum width.
|
/// Set both the minimum and maximum width.
|
||||||
@@ -556,6 +558,7 @@ impl Ui {
|
|||||||
// Layout related measures:
|
// Layout related measures:
|
||||||
|
|
||||||
/// The available space at the moment, given the current cursor.
|
/// The available space at the moment, given the current cursor.
|
||||||
|
///
|
||||||
/// This how much more space we can take up without overflowing our parent.
|
/// This how much more space we can take up without overflowing our parent.
|
||||||
/// Shrinks as widgets allocate space and the cursor moves.
|
/// Shrinks as widgets allocate space and the cursor moves.
|
||||||
/// A small size should be interpreted as "as little as possible".
|
/// A small size should be interpreted as "as little as possible".
|
||||||
@@ -564,19 +567,30 @@ impl Ui {
|
|||||||
self.placer.available_size()
|
self.placer.available_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The available width at the moment, given the current cursor.
|
||||||
|
///
|
||||||
|
/// See [`Self::available_size`] for more information.
|
||||||
pub fn available_width(&self) -> f32 {
|
pub fn available_width(&self) -> f32 {
|
||||||
self.available_size().x
|
self.available_size().x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The available height at the moment, given the current cursor.
|
||||||
|
///
|
||||||
|
/// See [`Self::available_size`] for more information.
|
||||||
pub fn available_height(&self) -> f32 {
|
pub fn available_height(&self) -> f32 {
|
||||||
self.available_size().y
|
self.available_size().y
|
||||||
}
|
}
|
||||||
|
|
||||||
/// In case of a wrapping layout, how much space is left on this row/column?
|
/// In case of a wrapping layout, how much space is left on this row/column?
|
||||||
|
///
|
||||||
|
/// If the layout does not wrap, this will return the same value as [`Self::available_size`].
|
||||||
pub fn available_size_before_wrap(&self) -> Vec2 {
|
pub fn available_size_before_wrap(&self) -> Vec2 {
|
||||||
self.placer.available_rect_before_wrap().size()
|
self.placer.available_rect_before_wrap().size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// In case of a wrapping layout, how much space is left on this row/column?
|
||||||
|
///
|
||||||
|
/// If the layout does not wrap, this will return the same value as [`Self::available_size`].
|
||||||
pub fn available_rect_before_wrap(&self) -> Rect {
|
pub fn available_rect_before_wrap(&self) -> Rect {
|
||||||
self.placer.available_rect_before_wrap()
|
self.placer.available_rect_before_wrap()
|
||||||
}
|
}
|
||||||
@@ -966,7 +980,7 @@ impl Ui {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn scroll_to_rect(&self, rect: Rect, align: Option<Align>) {
|
pub fn scroll_to_rect(&self, rect: Rect, align: Option<Align>) {
|
||||||
for d in 0..2 {
|
for d in 0..2 {
|
||||||
let range = rect.min[d]..=rect.max[d];
|
let range = Rangef::new(rect.min[d], rect.max[d]);
|
||||||
self.ctx()
|
self.ctx()
|
||||||
.frame_state_mut(|state| state.scroll_target[d] = Some((range, align)));
|
.frame_state_mut(|state| state.scroll_target[d] = Some((range, align)));
|
||||||
}
|
}
|
||||||
@@ -996,9 +1010,9 @@ impl Ui {
|
|||||||
pub fn scroll_to_cursor(&self, align: Option<Align>) {
|
pub fn scroll_to_cursor(&self, align: Option<Align>) {
|
||||||
let target = self.next_widget_position();
|
let target = self.next_widget_position();
|
||||||
for d in 0..2 {
|
for d in 0..2 {
|
||||||
let target = target[d];
|
let target = Rangef::point(target[d]);
|
||||||
self.ctx()
|
self.ctx()
|
||||||
.frame_state_mut(|state| state.scroll_target[d] = Some((target..=target, align)));
|
.frame_state_mut(|state| state.scroll_target[d] = Some((target, align)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2231,3 +2245,9 @@ impl Ui {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ui_impl_send_sync() {
|
||||||
|
fn assert_send_sync<T: Send + Sync>() {}
|
||||||
|
assert_send_sync::<Ui>();
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ pub struct Button {
|
|||||||
text: WidgetText,
|
text: WidgetText,
|
||||||
shortcut_text: WidgetText,
|
shortcut_text: WidgetText,
|
||||||
wrap: Option<bool>,
|
wrap: Option<bool>,
|
||||||
|
|
||||||
/// None means default for interact
|
/// None means default for interact
|
||||||
fill: Option<Color32>,
|
fill: Option<Color32>,
|
||||||
stroke: Option<Stroke>,
|
stroke: Option<Stroke>,
|
||||||
|
|||||||
@@ -234,17 +234,17 @@ fn color_text_ui(ui: &mut Ui, color: impl Into<Color32>, alpha: Alpha) {
|
|||||||
|
|
||||||
if ui.button("📋").on_hover_text("Click to copy").clicked() {
|
if ui.button("📋").on_hover_text("Click to copy").clicked() {
|
||||||
if alpha == Alpha::Opaque {
|
if alpha == Alpha::Opaque {
|
||||||
ui.output_mut(|o| o.copied_text = format!("{}, {}, {}", r, g, b));
|
ui.output_mut(|o| o.copied_text = format!("{r}, {g}, {b}"));
|
||||||
} else {
|
} else {
|
||||||
ui.output_mut(|o| o.copied_text = format!("{}, {}, {}, {}", r, g, b, a));
|
ui.output_mut(|o| o.copied_text = format!("{r}, {g}, {b}, {a}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if alpha == Alpha::Opaque {
|
if alpha == Alpha::Opaque {
|
||||||
ui.label(format!("rgb({}, {}, {})", r, g, b))
|
ui.label(format!("rgb({r}, {g}, {b})"))
|
||||||
.on_hover_text("Red Green Blue");
|
.on_hover_text("Red Green Blue");
|
||||||
} else {
|
} else {
|
||||||
ui.label(format!("rgba({}, {}, {}, {})", r, g, b, a))
|
ui.label(format!("rgba({r}, {g}, {b}, {a})"))
|
||||||
.on_hover_text("Red Green Blue with premultiplied Alpha");
|
.on_hover_text("Red Green Blue with premultiplied Alpha");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use crate::*;
|
|||||||
pub(crate) struct MonoState {
|
pub(crate) struct MonoState {
|
||||||
last_dragged_id: Option<Id>,
|
last_dragged_id: Option<Id>,
|
||||||
last_dragged_value: Option<f64>,
|
last_dragged_value: Option<f64>,
|
||||||
|
|
||||||
/// For temporary edit of a [`DragValue`] value.
|
/// For temporary edit of a [`DragValue`] value.
|
||||||
/// Couples with the current focus id.
|
/// Couples with the current focus id.
|
||||||
edit_string: Option<String>,
|
edit_string: Option<String>,
|
||||||
@@ -63,6 +64,7 @@ pub struct DragValue<'a> {
|
|||||||
max_decimals: Option<usize>,
|
max_decimals: Option<usize>,
|
||||||
custom_formatter: Option<NumFormatter<'a>>,
|
custom_formatter: Option<NumFormatter<'a>>,
|
||||||
custom_parser: Option<NumParser<'a>>,
|
custom_parser: Option<NumParser<'a>>,
|
||||||
|
update_while_editing: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DragValue<'a> {
|
impl<'a> DragValue<'a> {
|
||||||
@@ -94,6 +96,7 @@ impl<'a> DragValue<'a> {
|
|||||||
max_decimals: None,
|
max_decimals: None,
|
||||||
custom_formatter: None,
|
custom_formatter: None,
|
||||||
custom_parser: None,
|
custom_parser: None,
|
||||||
|
update_while_editing: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,6 +355,15 @@ impl<'a> DragValue<'a> {
|
|||||||
}
|
}
|
||||||
.custom_parser(|s| i64::from_str_radix(s, 16).map(|n| n as f64).ok())
|
.custom_parser(|s| i64::from_str_radix(s, 16).map(|n| n as f64).ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the value on each key press when text-editing the value.
|
||||||
|
///
|
||||||
|
/// Default: `true`.
|
||||||
|
/// If `false`, the value will only be updated when user presses enter or deselects the value.
|
||||||
|
pub fn update_while_editing(mut self, update: bool) -> Self {
|
||||||
|
self.update_while_editing = update;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Widget for DragValue<'a> {
|
impl<'a> Widget for DragValue<'a> {
|
||||||
@@ -366,6 +378,7 @@ impl<'a> Widget for DragValue<'a> {
|
|||||||
max_decimals,
|
max_decimals,
|
||||||
custom_formatter,
|
custom_formatter,
|
||||||
custom_parser,
|
custom_parser,
|
||||||
|
update_while_editing,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let shift = ui.input(|i| i.modifiers.shift_only());
|
let shift = ui.input(|i| i.modifiers.shift_only());
|
||||||
@@ -392,7 +405,9 @@ impl<'a> Widget for DragValue<'a> {
|
|||||||
|
|
||||||
let auto_decimals = (aim_rad / speed.abs()).log10().ceil().clamp(0.0, 15.0) as usize;
|
let auto_decimals = (aim_rad / speed.abs()).log10().ceil().clamp(0.0, 15.0) as usize;
|
||||||
let auto_decimals = auto_decimals + is_slow_speed as usize;
|
let auto_decimals = auto_decimals + is_slow_speed as usize;
|
||||||
let max_decimals = max_decimals.unwrap_or(auto_decimals + 2);
|
let max_decimals = max_decimals
|
||||||
|
.unwrap_or(auto_decimals + 2)
|
||||||
|
.at_least(min_decimals);
|
||||||
let auto_decimals = auto_decimals.clamp(min_decimals, max_decimals);
|
let auto_decimals = auto_decimals.clamp(min_decimals, max_decimals);
|
||||||
|
|
||||||
let change = ui.input_mut(|input| {
|
let change = ui.input_mut(|input| {
|
||||||
@@ -475,9 +490,15 @@ impl<'a> Widget for DragValue<'a> {
|
|||||||
.desired_width(ui.spacing().interact_size.x)
|
.desired_width(ui.spacing().interact_size.x)
|
||||||
.font(text_style),
|
.font(text_style),
|
||||||
);
|
);
|
||||||
// Only update the value when the user presses enter, or clicks elsewhere. NOT every frame.
|
|
||||||
// See https://github.com/emilk/egui/issues/2687
|
let update = if update_while_editing {
|
||||||
if response.lost_focus() {
|
// Update when the edit content has changed.
|
||||||
|
response.changed()
|
||||||
|
} else {
|
||||||
|
// Update only when the edit has lost focus.
|
||||||
|
response.lost_focus()
|
||||||
|
};
|
||||||
|
if update {
|
||||||
let parsed_value = match custom_parser {
|
let parsed_value = match custom_parser {
|
||||||
Some(parser) => parser(&value_text),
|
Some(parser) => parser(&value_text),
|
||||||
None => value_text.parse().ok(),
|
None => value_text.parse().ok(),
|
||||||
@@ -606,7 +627,7 @@ impl<'a> Widget for DragValue<'a> {
|
|||||||
// The value is exposed as a string by the text edit widget
|
// The value is exposed as a string by the text edit widget
|
||||||
// when in edit mode.
|
// when in edit mode.
|
||||||
if !is_kb_editing {
|
if !is_kb_editing {
|
||||||
let value_text = format!("{}{}{}", prefix, value_text, suffix);
|
let value_text = format!("{prefix}{value_text}{suffix}");
|
||||||
builder.set_value(value_text);
|
builder.set_value(value_text);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -760,15 +760,22 @@ impl PlotItem for Text {
|
|||||||
/// A set of points.
|
/// A set of points.
|
||||||
pub struct Points {
|
pub struct Points {
|
||||||
pub(super) series: PlotPoints,
|
pub(super) series: PlotPoints,
|
||||||
|
|
||||||
pub(super) shape: MarkerShape,
|
pub(super) shape: MarkerShape,
|
||||||
|
|
||||||
/// Color of the marker. `Color32::TRANSPARENT` means that it will be picked automatically.
|
/// Color of the marker. `Color32::TRANSPARENT` means that it will be picked automatically.
|
||||||
pub(super) color: Color32,
|
pub(super) color: Color32,
|
||||||
|
|
||||||
/// Whether to fill the marker. Does not apply to all types.
|
/// Whether to fill the marker. Does not apply to all types.
|
||||||
pub(super) filled: bool,
|
pub(super) filled: bool,
|
||||||
|
|
||||||
/// The maximum extent of the marker from its center.
|
/// The maximum extent of the marker from its center.
|
||||||
pub(super) radius: f32,
|
pub(super) radius: f32,
|
||||||
|
|
||||||
pub(super) name: String,
|
pub(super) name: String,
|
||||||
|
|
||||||
pub(super) highlight: bool,
|
pub(super) highlight: bool,
|
||||||
|
|
||||||
pub(super) stems: Option<f32>,
|
pub(super) stems: Option<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -997,6 +1004,7 @@ impl PlotItem for Points {
|
|||||||
pub struct Arrows {
|
pub struct Arrows {
|
||||||
pub(super) origins: PlotPoints,
|
pub(super) origins: PlotPoints,
|
||||||
pub(super) tips: PlotPoints,
|
pub(super) tips: PlotPoints,
|
||||||
|
pub(super) tip_length: Option<f32>,
|
||||||
pub(super) color: Color32,
|
pub(super) color: Color32,
|
||||||
pub(super) name: String,
|
pub(super) name: String,
|
||||||
pub(super) highlight: bool,
|
pub(super) highlight: bool,
|
||||||
@@ -1007,6 +1015,7 @@ impl Arrows {
|
|||||||
Self {
|
Self {
|
||||||
origins: origins.into(),
|
origins: origins.into(),
|
||||||
tips: tips.into(),
|
tips: tips.into(),
|
||||||
|
tip_length: None,
|
||||||
color: Color32::TRANSPARENT,
|
color: Color32::TRANSPARENT,
|
||||||
name: Default::default(),
|
name: Default::default(),
|
||||||
highlight: false,
|
highlight: false,
|
||||||
@@ -1019,6 +1028,12 @@ impl Arrows {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the length of the arrow tips
|
||||||
|
pub fn tip_length(mut self, tip_length: f32) -> Self {
|
||||||
|
self.tip_length = Some(tip_length);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the arrows' color.
|
/// Set the arrows' color.
|
||||||
pub fn color(mut self, color: impl Into<Color32>) -> Self {
|
pub fn color(mut self, color: impl Into<Color32>) -> Self {
|
||||||
self.color = color.into();
|
self.color = color.into();
|
||||||
@@ -1044,6 +1059,7 @@ impl PlotItem for Arrows {
|
|||||||
let Self {
|
let Self {
|
||||||
origins,
|
origins,
|
||||||
tips,
|
tips,
|
||||||
|
tip_length,
|
||||||
color,
|
color,
|
||||||
highlight,
|
highlight,
|
||||||
..
|
..
|
||||||
@@ -1062,7 +1078,11 @@ impl PlotItem for Arrows {
|
|||||||
.for_each(|(origin, tip)| {
|
.for_each(|(origin, tip)| {
|
||||||
let vector = tip - origin;
|
let vector = tip - origin;
|
||||||
let rot = Rot2::from_angle(std::f32::consts::TAU / 10.0);
|
let rot = Rot2::from_angle(std::f32::consts::TAU / 10.0);
|
||||||
let tip_length = vector.length() / 4.0;
|
let tip_length = if let Some(tip_length) = tip_length {
|
||||||
|
*tip_length
|
||||||
|
} else {
|
||||||
|
vector.length() / 4.0
|
||||||
|
};
|
||||||
let tip = origin + vector;
|
let tip = origin + vector;
|
||||||
let dir = vector.normalized();
|
let dir = vector.normalized();
|
||||||
shapes.push(Shape::line_segment([origin, tip], stroke));
|
shapes.push(Shape::line_segment([origin, tip], stroke));
|
||||||
@@ -1119,6 +1139,7 @@ pub struct PlotImage {
|
|||||||
pub(super) tint: Color32,
|
pub(super) tint: Color32,
|
||||||
pub(super) highlight: bool,
|
pub(super) highlight: bool,
|
||||||
pub(super) name: String,
|
pub(super) name: String,
|
||||||
|
pub(crate) rotation: Option<(f32, Vec2)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlotImage {
|
impl PlotImage {
|
||||||
@@ -1137,6 +1158,7 @@ impl PlotImage {
|
|||||||
size: size.into(),
|
size: size.into(),
|
||||||
bg_fill: Default::default(),
|
bg_fill: Default::default(),
|
||||||
tint: Color32::WHITE,
|
tint: Color32::WHITE,
|
||||||
|
rotation: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1175,6 +1197,17 @@ impl PlotImage {
|
|||||||
self.name = name.to_string();
|
self.name = name.to_string();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rotate the image about an origin by some angle
|
||||||
|
///
|
||||||
|
/// Positive angle is clockwise.
|
||||||
|
/// Origin is a vector in normalized UV space ((0,0) in top-left, (1,1) bottom right).
|
||||||
|
///
|
||||||
|
/// To rotate about the center you can pass `Vec2::splat(0.5)` as the origin.
|
||||||
|
pub fn rotate(mut self, angle: f32, origin: Vec2) -> Self {
|
||||||
|
self.rotation = Some((angle, origin));
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlotItem for PlotImage {
|
impl PlotItem for PlotImage {
|
||||||
@@ -1202,11 +1235,14 @@ impl PlotItem for PlotImage {
|
|||||||
let right_bottom_tf = transform.position_from_point(&right_bottom);
|
let right_bottom_tf = transform.position_from_point(&right_bottom);
|
||||||
Rect::from_two_pos(left_top_tf, right_bottom_tf)
|
Rect::from_two_pos(left_top_tf, right_bottom_tf)
|
||||||
};
|
};
|
||||||
Image::new(*texture_id, *size)
|
let mut image = Image::new(*texture_id, *size)
|
||||||
.bg_fill(*bg_fill)
|
.bg_fill(*bg_fill)
|
||||||
.tint(*tint)
|
.tint(*tint)
|
||||||
.uv(*uv)
|
.uv(*uv);
|
||||||
.paint_at(ui, rect);
|
if let Some((angle, origin)) = self.rotation {
|
||||||
|
image = image.rotate(angle, origin);
|
||||||
|
}
|
||||||
|
image.paint_at(ui, rect);
|
||||||
if *highlight {
|
if *highlight {
|
||||||
shapes.push(Shape::rect_stroke(
|
shapes.push(Shape::rect_stroke(
|
||||||
rect,
|
rect,
|
||||||
@@ -1261,8 +1297,10 @@ pub struct BarChart {
|
|||||||
pub(super) bars: Vec<Bar>,
|
pub(super) bars: Vec<Bar>,
|
||||||
pub(super) default_color: Color32,
|
pub(super) default_color: Color32,
|
||||||
pub(super) name: String,
|
pub(super) name: String,
|
||||||
|
|
||||||
/// A custom element formatter
|
/// A custom element formatter
|
||||||
pub(super) element_formatter: Option<Box<dyn Fn(&Bar, &BarChart) -> String>>,
|
pub(super) element_formatter: Option<Box<dyn Fn(&Bar, &BarChart) -> String>>,
|
||||||
|
|
||||||
highlight: bool,
|
highlight: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1431,8 +1469,10 @@ pub struct BoxPlot {
|
|||||||
pub(super) boxes: Vec<BoxElem>,
|
pub(super) boxes: Vec<BoxElem>,
|
||||||
pub(super) default_color: Color32,
|
pub(super) default_color: Color32,
|
||||||
pub(super) name: String,
|
pub(super) name: String,
|
||||||
|
|
||||||
/// A custom element formatter
|
/// A custom element formatter
|
||||||
pub(super) element_formatter: Option<Box<dyn Fn(&BoxElem, &BoxPlot) -> String>>,
|
pub(super) element_formatter: Option<Box<dyn Fn(&BoxElem, &BoxPlot) -> String>>,
|
||||||
|
|
||||||
highlight: bool,
|
highlight: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1692,7 +1732,7 @@ pub(super) fn rulers_at_value(
|
|||||||
let mut prefix = String::new();
|
let mut prefix = String::new();
|
||||||
|
|
||||||
if !name.is_empty() {
|
if !name.is_empty() {
|
||||||
prefix = format!("{}\n", name);
|
prefix = format!("{name}\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
let text = {
|
let text = {
|
||||||
|
|||||||
@@ -125,8 +125,8 @@ impl ToString for LineStyle {
|
|||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
LineStyle::Solid => "Solid".into(),
|
LineStyle::Solid => "Solid".into(),
|
||||||
LineStyle::Dotted { spacing } => format!("Dotted{}Px", spacing),
|
LineStyle::Dotted { spacing } => format!("Dotted{spacing}Px"),
|
||||||
LineStyle::Dashed { length } => format!("Dashed{}Px", length),
|
LineStyle::Dashed { length } => format!("Dashed{length}Px"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,9 +101,11 @@ struct PlotMemory {
|
|||||||
/// Indicates if the user has modified the bounds, for example by moving or zooming,
|
/// Indicates if the user has modified the bounds, for example by moving or zooming,
|
||||||
/// or if the bounds should be calculated based by included point or auto bounds.
|
/// or if the bounds should be calculated based by included point or auto bounds.
|
||||||
bounds_modified: AxisBools,
|
bounds_modified: AxisBools,
|
||||||
|
|
||||||
hovered_entry: Option<String>,
|
hovered_entry: Option<String>,
|
||||||
hidden_items: ahash::HashSet<String>,
|
hidden_items: ahash::HashSet<String>,
|
||||||
last_plot_transform: PlotTransform,
|
last_plot_transform: PlotTransform,
|
||||||
|
|
||||||
/// Allows to remember the first click position when performing a boxed zoom
|
/// Allows to remember the first click position when performing a boxed zoom
|
||||||
last_click_pos_for_zoom: Option<Pos2>,
|
last_click_pos_for_zoom: Option<Pos2>,
|
||||||
}
|
}
|
||||||
@@ -864,7 +866,7 @@ impl Plot {
|
|||||||
delta.y = 0.0;
|
delta.y = 0.0;
|
||||||
}
|
}
|
||||||
transform.translate_bounds(delta);
|
transform.translate_bounds(delta);
|
||||||
bounds_modified = true.into();
|
bounds_modified = allow_drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zooming
|
// Zooming
|
||||||
@@ -935,7 +937,7 @@ impl Plot {
|
|||||||
}
|
}
|
||||||
if zoom_factor != Vec2::splat(1.0) {
|
if zoom_factor != Vec2::splat(1.0) {
|
||||||
transform.zoom(zoom_factor, hover_pos);
|
transform.zoom(zoom_factor, hover_pos);
|
||||||
bounds_modified = true.into();
|
bounds_modified = allow_zoom;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if allow_scroll {
|
if allow_scroll {
|
||||||
@@ -1081,17 +1083,25 @@ impl PlotUi {
|
|||||||
.push(BoundsModification::Translate(delta_pos));
|
.push(BoundsModification::Translate(delta_pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Can be used to check if the plot was hovered or clicked.
|
||||||
|
pub fn response(&self) -> &Response {
|
||||||
|
&self.response
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if the plot area is currently hovered.
|
/// Returns `true` if the plot area is currently hovered.
|
||||||
|
#[deprecated = "Use plot_ui.response().hovered()"]
|
||||||
pub fn plot_hovered(&self) -> bool {
|
pub fn plot_hovered(&self) -> bool {
|
||||||
self.response.hovered()
|
self.response.hovered()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the plot was clicked by the primary button.
|
/// Returns `true` if the plot was clicked by the primary button.
|
||||||
|
#[deprecated = "Use plot_ui.response().clicked()"]
|
||||||
pub fn plot_clicked(&self) -> bool {
|
pub fn plot_clicked(&self) -> bool {
|
||||||
self.response.clicked()
|
self.response.clicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the plot was clicked by the secondary button.
|
/// Returns `true` if the plot was clicked by the secondary button.
|
||||||
|
#[deprecated = "Use plot_ui.response().secondary_clicked()"]
|
||||||
pub fn plot_secondary_clicked(&self) -> bool {
|
pub fn plot_secondary_clicked(&self) -> bool {
|
||||||
self.response.secondary_clicked()
|
self.response.secondary_clicked()
|
||||||
}
|
}
|
||||||
@@ -1443,7 +1453,7 @@ impl PreparedPlot {
|
|||||||
let axis_range = match axis {
|
let axis_range = match axis {
|
||||||
0 => bounds.range_x(),
|
0 => bounds.range_x(),
|
||||||
1 => bounds.range_y(),
|
1 => bounds.range_y(),
|
||||||
_ => panic!("Axis {} does not exist.", axis),
|
_ => panic!("Axis {axis} does not exist."),
|
||||||
};
|
};
|
||||||
|
|
||||||
let font_id = TextStyle::Body.resolve(ui.style());
|
let font_id = TextStyle::Body.resolve(ui.style());
|
||||||
@@ -1666,7 +1676,7 @@ pub fn format_number(number: f64, num_decimals: usize) -> String {
|
|||||||
let is_integral = number as i64 as f64 == number;
|
let is_integral = number as i64 as f64 == number;
|
||||||
if is_integral {
|
if is_integral {
|
||||||
// perfect integer - show it as such:
|
// perfect integer - show it as such:
|
||||||
format!("{:.0}", number)
|
format!("{number:.0}")
|
||||||
} else {
|
} else {
|
||||||
// make sure we tell the user it is not an integer by always showing a decimal or two:
|
// make sure we tell the user it is not an integer by always showing a decimal or two:
|
||||||
format!("{:.*}", num_decimals.at_least(1), number)
|
format!("{:.*}", num_decimals.at_least(1), number)
|
||||||
|
|||||||
@@ -77,8 +77,10 @@ pub struct Slider<'a> {
|
|||||||
prefix: String,
|
prefix: String,
|
||||||
suffix: String,
|
suffix: String,
|
||||||
text: WidgetText,
|
text: WidgetText,
|
||||||
|
|
||||||
/// Sets the minimal step of the widget value
|
/// Sets the minimal step of the widget value
|
||||||
step: Option<f64>,
|
step: Option<f64>,
|
||||||
|
|
||||||
drag_value_speed: Option<f64>,
|
drag_value_speed: Option<f64>,
|
||||||
min_decimals: usize,
|
min_decimals: usize,
|
||||||
max_decimals: Option<usize>,
|
max_decimals: Option<usize>,
|
||||||
@@ -524,12 +526,12 @@ impl<'a> Slider<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// For instance, `position` is the mouse position and `position_range` is the physical location of the slider on the screen.
|
/// For instance, `position` is the mouse position and `position_range` is the physical location of the slider on the screen.
|
||||||
fn value_from_position(&self, position: f32, position_range: RangeInclusive<f32>) -> f64 {
|
fn value_from_position(&self, position: f32, position_range: Rangef) -> f64 {
|
||||||
let normalized = remap_clamp(position, position_range, 0.0..=1.0) as f64;
|
let normalized = remap_clamp(position, position_range, 0.0..=1.0) as f64;
|
||||||
value_from_normalized(normalized, self.range(), &self.spec)
|
value_from_normalized(normalized, self.range(), &self.spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn position_from_value(&self, value: f64, position_range: RangeInclusive<f32>) -> f32 {
|
fn position_from_value(&self, value: f64, position_range: Rangef) -> f32 {
|
||||||
let normalized = normalized_from_value(value, self.range(), &self.spec);
|
let normalized = normalized_from_value(value, self.range(), &self.spec);
|
||||||
lerp(position_range, normalized as f32)
|
lerp(position_range, normalized as f32)
|
||||||
}
|
}
|
||||||
@@ -555,11 +557,11 @@ impl<'a> Slider<'a> {
|
|||||||
let new_value = if self.smart_aim {
|
let new_value = if self.smart_aim {
|
||||||
let aim_radius = ui.input(|i| i.aim_radius());
|
let aim_radius = ui.input(|i| i.aim_radius());
|
||||||
emath::smart_aim::best_in_range_f64(
|
emath::smart_aim::best_in_range_f64(
|
||||||
self.value_from_position(position - aim_radius, position_range.clone()),
|
self.value_from_position(position - aim_radius, position_range),
|
||||||
self.value_from_position(position + aim_radius, position_range.clone()),
|
self.value_from_position(position + aim_radius, position_range),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
self.value_from_position(position, position_range.clone())
|
self.value_from_position(position, position_range)
|
||||||
};
|
};
|
||||||
self.set_value(new_value);
|
self.set_value(new_value);
|
||||||
}
|
}
|
||||||
@@ -594,18 +596,18 @@ impl<'a> Slider<'a> {
|
|||||||
|
|
||||||
if kb_step != 0.0 {
|
if kb_step != 0.0 {
|
||||||
let prev_value = self.get_value();
|
let prev_value = self.get_value();
|
||||||
let prev_position = self.position_from_value(prev_value, position_range.clone());
|
let prev_position = self.position_from_value(prev_value, position_range);
|
||||||
let new_position = prev_position + kb_step;
|
let new_position = prev_position + kb_step;
|
||||||
let new_value = match self.step {
|
let new_value = match self.step {
|
||||||
Some(step) => prev_value + (kb_step as f64 * step),
|
Some(step) => prev_value + (kb_step as f64 * step),
|
||||||
None if self.smart_aim => {
|
None if self.smart_aim => {
|
||||||
let aim_radius = ui.input(|i| i.aim_radius());
|
let aim_radius = ui.input(|i| i.aim_radius());
|
||||||
emath::smart_aim::best_in_range_f64(
|
emath::smart_aim::best_in_range_f64(
|
||||||
self.value_from_position(new_position - aim_radius, position_range.clone()),
|
self.value_from_position(new_position - aim_radius, position_range),
|
||||||
self.value_from_position(new_position + aim_radius, position_range.clone()),
|
self.value_from_position(new_position + aim_radius, position_range),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => self.value_from_position(new_position, position_range.clone()),
|
_ => self.value_from_position(new_position, position_range),
|
||||||
};
|
};
|
||||||
self.set_value(new_value);
|
self.set_value(new_value);
|
||||||
}
|
}
|
||||||
@@ -686,15 +688,11 @@ impl<'a> Slider<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn position_range(&self, rect: &Rect) -> RangeInclusive<f32> {
|
fn position_range(&self, rect: &Rect) -> Rangef {
|
||||||
let handle_radius = self.handle_radius(rect);
|
let handle_radius = self.handle_radius(rect);
|
||||||
match self.orientation {
|
match self.orientation {
|
||||||
SliderOrientation::Horizontal => {
|
SliderOrientation::Horizontal => rect.x_range().shrink(handle_radius),
|
||||||
(rect.left() + handle_radius)..=(rect.right() - handle_radius)
|
SliderOrientation::Vertical => rect.y_range().shrink(handle_radius),
|
||||||
}
|
|
||||||
SliderOrientation::Vertical => {
|
|
||||||
(rect.bottom() - handle_radius)..=(rect.top() + handle_radius)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -726,7 +724,7 @@ impl<'a> Slider<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_ui(&mut self, ui: &mut Ui, position_range: RangeInclusive<f32>) -> Response {
|
fn value_ui(&mut self, ui: &mut Ui, position_range: Rangef) -> Response {
|
||||||
// If [`DragValue`] is controlled from the keyboard and `step` is defined, set speed to `step`
|
// If [`DragValue`] is controlled from the keyboard and `step` is defined, set speed to `step`
|
||||||
let change = ui.input(|input| {
|
let change = ui.input(|input| {
|
||||||
input.num_presses(Key::ArrowUp) as i32 + input.num_presses(Key::ArrowRight) as i32
|
input.num_presses(Key::ArrowUp) as i32 + input.num_presses(Key::ArrowRight) as i32
|
||||||
@@ -740,7 +738,7 @@ impl<'a> Slider<'a> {
|
|||||||
step
|
step
|
||||||
} else {
|
} else {
|
||||||
self.drag_value_speed
|
self.drag_value_speed
|
||||||
.unwrap_or_else(|| self.current_gradient(&position_range))
|
.unwrap_or_else(|| self.current_gradient(position_range))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut value = self.get_value();
|
let mut value = self.get_value();
|
||||||
@@ -767,12 +765,11 @@ impl<'a> Slider<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// delta(value) / delta(points)
|
/// delta(value) / delta(points)
|
||||||
fn current_gradient(&mut self, position_range: &RangeInclusive<f32>) -> f64 {
|
fn current_gradient(&mut self, position_range: Rangef) -> f64 {
|
||||||
// TODO(emilk): handle clamping
|
// TODO(emilk): handle clamping
|
||||||
let value = self.get_value();
|
let value = self.get_value();
|
||||||
let value_from_pos =
|
let value_from_pos = |position: f32| self.value_from_position(position, position_range);
|
||||||
|position: f32| self.value_from_position(position, position_range.clone());
|
let pos_from_value = |value: f64| self.position_from_value(value, position_range);
|
||||||
let pos_from_value = |value: f64| self.position_from_value(value, position_range.clone());
|
|
||||||
let left_value = value_from_pos(pos_from_value(value) - 0.5);
|
let left_value = value_from_pos(pos_from_value(value) - 0.5);
|
||||||
let right_value = value_from_pos(pos_from_value(value) + 0.5);
|
let right_value = value_from_pos(pos_from_value(value) + 0.5);
|
||||||
right_value - left_value
|
right_value - left_value
|
||||||
|
|||||||
@@ -1138,7 +1138,7 @@ fn paint_cursor_end(
|
|||||||
galley: &Galley,
|
galley: &Galley,
|
||||||
cursor: &Cursor,
|
cursor: &Cursor,
|
||||||
) -> Rect {
|
) -> Rect {
|
||||||
let stroke = ui.visuals().selection.stroke;
|
let stroke = ui.visuals().text_cursor;
|
||||||
|
|
||||||
let mut cursor_pos = galley.pos_from_cursor(cursor).translate(pos.to_vec2());
|
let mut cursor_pos = galley.pos_from_cursor(cursor).translate(pos.to_vec2());
|
||||||
cursor_pos.max.y = cursor_pos.max.y.at_least(cursor_pos.min.y + row_height); // Handle completely empty galleys
|
cursor_pos.max.y = cursor_pos.max.y.at_least(cursor_pos.min.y + row_height); // Handle completely empty galleys
|
||||||
@@ -1147,10 +1147,7 @@ fn paint_cursor_end(
|
|||||||
let top = cursor_pos.center_top();
|
let top = cursor_pos.center_top();
|
||||||
let bottom = cursor_pos.center_bottom();
|
let bottom = cursor_pos.center_bottom();
|
||||||
|
|
||||||
painter.line_segment(
|
painter.line_segment([top, bottom], (stroke.width, stroke.color));
|
||||||
[top, bottom],
|
|
||||||
(ui.visuals().text_cursor_width, stroke.color),
|
|
||||||
);
|
|
||||||
|
|
||||||
if false {
|
if false {
|
||||||
// Roof/floor:
|
// Roof/floor:
|
||||||
@@ -1185,7 +1182,7 @@ fn insert_text(
|
|||||||
if char_limit < usize::MAX {
|
if char_limit < usize::MAX {
|
||||||
let mut new_string = text_to_insert;
|
let mut new_string = text_to_insert;
|
||||||
// Avoid subtract with overflow panic
|
// Avoid subtract with overflow panic
|
||||||
let cutoff = char_limit.saturating_sub(text.as_str().len());
|
let cutoff = char_limit.saturating_sub(text.as_str().chars().count());
|
||||||
|
|
||||||
new_string = match new_string.char_indices().nth(cutoff) {
|
new_string = match new_string.char_indices().nth(cutoff) {
|
||||||
None => new_string,
|
None => new_string,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ version = "0.22.0"
|
|||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.67"
|
||||||
publish = false
|
publish = false
|
||||||
default-run = "egui_demo_app"
|
default-run = "egui_demo_app"
|
||||||
|
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ fn ui_url(ui: &mut egui::Ui, frame: &mut eframe::Frame, url: &mut String) -> boo
|
|||||||
if ui.button("Random image").clicked() {
|
if ui.button("Random image").clicked() {
|
||||||
let seed = ui.input(|i| i.time);
|
let seed = ui.input(|i| i.time);
|
||||||
let side = 640;
|
let side = 640;
|
||||||
*url = format!("https://picsum.photos/seed/{}/{}", seed, side);
|
*url = format!("https://picsum.photos/seed/{seed}/{side}");
|
||||||
trigger_fetch = true;
|
trigger_fetch = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -232,8 +232,7 @@ impl BackendPanel {
|
|||||||
if ui
|
if ui
|
||||||
.add_enabled(enabled, egui::Button::new("Reset"))
|
.add_enabled(enabled, egui::Button::new("Reset"))
|
||||||
.on_hover_text(format!(
|
.on_hover_text(format!(
|
||||||
"Reset scale to native value ({:.1})",
|
"Reset scale to native value ({native_pixels_per_point:.1})"
|
||||||
native_pixels_per_point
|
|
||||||
))
|
))
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
@@ -441,7 +440,7 @@ impl EguiWindows {
|
|||||||
.stick_to_bottom(true)
|
.stick_to_bottom(true)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
for event in output_event_history {
|
for event in output_event_history {
|
||||||
ui.label(format!("{:?}", event));
|
ui.label(format!("{event:?}"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ impl WrapApp {
|
|||||||
{
|
{
|
||||||
selected_anchor = anchor;
|
selected_anchor = anchor;
|
||||||
if frame.is_web() {
|
if frame.is_web() {
|
||||||
ui.output_mut(|o| o.open_url(format!("#{}", anchor)));
|
ui.output_mut(|o| o.open_url(format!("#{anchor}")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ version = "0.22.0"
|
|||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "Example library for egui"
|
description = "Example library for egui"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.67"
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/egui_demo_lib"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/egui_demo_lib"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -83,11 +83,11 @@ fn about_immediate_mode(ui: &mut egui::Ui) {
|
|||||||
fn links(ui: &mut egui::Ui) {
|
fn links(ui: &mut egui::Ui) {
|
||||||
use egui::special_emojis::{GITHUB, TWITTER};
|
use egui::special_emojis::{GITHUB, TWITTER};
|
||||||
ui.hyperlink_to(
|
ui.hyperlink_to(
|
||||||
format!("{} egui on GitHub", GITHUB),
|
format!("{GITHUB} egui on GitHub"),
|
||||||
"https://github.com/emilk/egui",
|
"https://github.com/emilk/egui",
|
||||||
);
|
);
|
||||||
ui.hyperlink_to(
|
ui.hyperlink_to(
|
||||||
format!("{} @ernerfeldt", TWITTER),
|
format!("{TWITTER} @ernerfeldt"),
|
||||||
"https://twitter.com/ernerfeldt",
|
"https://twitter.com/ernerfeldt",
|
||||||
);
|
);
|
||||||
ui.hyperlink_to("egui documentation", "https://docs.rs/egui/");
|
ui.hyperlink_to("egui documentation", "https://docs.rs/egui/");
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ impl CodeExample {
|
|||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
code_view_ui(ui, &format!("{:#?}", self));
|
code_view_ui(ui, &format!("{self:#?}"));
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
|
|||||||
@@ -251,11 +251,11 @@ impl DemoWindows {
|
|||||||
|
|
||||||
use egui::special_emojis::{GITHUB, TWITTER};
|
use egui::special_emojis::{GITHUB, TWITTER};
|
||||||
ui.hyperlink_to(
|
ui.hyperlink_to(
|
||||||
format!("{} egui on GitHub", GITHUB),
|
format!("{GITHUB} egui on GitHub"),
|
||||||
"https://github.com/emilk/egui",
|
"https://github.com/emilk/egui",
|
||||||
);
|
);
|
||||||
ui.hyperlink_to(
|
ui.hyperlink_to(
|
||||||
format!("{} @ernerfeldt", TWITTER),
|
format!("{TWITTER} @ernerfeldt"),
|
||||||
"https://twitter.com/ernerfeldt",
|
"https://twitter.com/ernerfeldt",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ impl LayoutTest {
|
|||||||
Direction::TopDown,
|
Direction::TopDown,
|
||||||
Direction::BottomUp,
|
Direction::BottomUp,
|
||||||
] {
|
] {
|
||||||
ui.radio_value(&mut self.layout.main_dir, dir, format!("{:?}", dir));
|
ui.radio_value(&mut self.layout.main_dir, dir, format!("{dir:?}"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ impl LayoutTest {
|
|||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("Cross Align:");
|
ui.label("Cross Align:");
|
||||||
for &align in &[Align::Min, Align::Center, Align::Max] {
|
for &align in &[Align::Min, Align::Center, Align::Max] {
|
||||||
ui.radio_value(&mut self.layout.cross_align, align, format!("{:?}", align));
|
ui.radio_value(&mut self.layout.cross_align, align, format!("{align:?}"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -455,7 +455,7 @@ impl Tree {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, mut tree)| {
|
.filter_map(|(i, mut tree)| {
|
||||||
if tree.ui_impl(ui, depth + 1, &format!("child #{}", i)) == Action::Keep {
|
if tree.ui_impl(ui, depth + 1, &format!("child #{i}")) == Action::Keep {
|
||||||
Some(tree)
|
Some(tree)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ impl super::View for MultiTouch {
|
|||||||
ui.label("Try touch gestures Pinch/Stretch, Rotation, and Pressure with 2+ fingers.");
|
ui.label("Try touch gestures Pinch/Stretch, Rotation, and Pressure with 2+ fingers.");
|
||||||
|
|
||||||
let num_touches = ui.input(|i| i.multi_touch().map_or(0, |mt| mt.num_touches));
|
let num_touches = ui.input(|i| i.multi_touch().map_or(0, |mt| mt.num_touches));
|
||||||
ui.label(format!("Current touches: {}", num_touches));
|
ui.label(format!("Current touches: {num_touches}"));
|
||||||
|
|
||||||
let color = if ui.visuals().dark_mode {
|
let color = if ui.visuals().dark_mode {
|
||||||
Color32::WHITE
|
Color32::WHITE
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ pub struct PlotDemo {
|
|||||||
charts_demo: ChartsDemo,
|
charts_demo: ChartsDemo,
|
||||||
items_demo: ItemsDemo,
|
items_demo: ItemsDemo,
|
||||||
interaction_demo: InteractionDemo,
|
interaction_demo: InteractionDemo,
|
||||||
custom_axes_demo: CustomAxisDemo,
|
custom_axes_demo: CustomAxesDemo,
|
||||||
linked_axes_demo: LinkedAxisDemo,
|
linked_axes_demo: LinkedAxesDemo,
|
||||||
open_panel: Panel,
|
open_panel: Panel,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,7 +326,7 @@ impl MarkerDemo {
|
|||||||
[5.0, 0.0 + y_offset],
|
[5.0, 0.0 + y_offset],
|
||||||
[6.0, 0.5 + y_offset],
|
[6.0, 0.5 + y_offset],
|
||||||
])
|
])
|
||||||
.name(format!("{:?}", marker))
|
.name(format!("{marker:?}"))
|
||||||
.filled(self.fill_markers)
|
.filled(self.fill_markers)
|
||||||
.radius(self.marker_radius)
|
.radius(self.marker_radius)
|
||||||
.shape(marker);
|
.shape(marker);
|
||||||
@@ -416,7 +416,7 @@ impl LegendDemo {
|
|||||||
ui.label("Position:");
|
ui.label("Position:");
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
Corner::all().for_each(|position| {
|
Corner::all().for_each(|position| {
|
||||||
ui.selectable_value(&mut config.position, position, format!("{:?}", position));
|
ui.selectable_value(&mut config.position, position, format!("{position:?}"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
@@ -448,19 +448,19 @@ impl LegendDemo {
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(PartialEq, Default)]
|
#[derive(PartialEq, Default)]
|
||||||
struct CustomAxisDemo {}
|
struct CustomAxesDemo {}
|
||||||
|
|
||||||
impl CustomAxisDemo {
|
impl CustomAxesDemo {
|
||||||
const MINS_PER_DAY: f64 = 24.0 * 60.0;
|
const MINS_PER_DAY: f64 = 24.0 * 60.0;
|
||||||
const MINS_PER_H: f64 = 60.0;
|
const MINS_PER_H: f64 = 60.0;
|
||||||
|
|
||||||
fn logistic_fn() -> Line {
|
fn logistic_fn() -> Line {
|
||||||
fn days(min: f64) -> f64 {
|
fn days(min: f64) -> f64 {
|
||||||
CustomAxisDemo::MINS_PER_DAY * min
|
CustomAxesDemo::MINS_PER_DAY * min
|
||||||
}
|
}
|
||||||
|
|
||||||
let values = PlotPoints::from_explicit_callback(
|
let values = PlotPoints::from_explicit_callback(
|
||||||
move |x| 1.0 / (1.0 + (-2.5 * (x / CustomAxisDemo::MINS_PER_DAY - 2.0)).exp()),
|
move |x| 1.0 / (1.0 + (-2.5 * (x / CustomAxesDemo::MINS_PER_DAY - 2.0)).exp()),
|
||||||
days(0.0)..days(5.0),
|
days(0.0)..days(5.0),
|
||||||
100,
|
100,
|
||||||
);
|
);
|
||||||
@@ -504,8 +504,8 @@ impl CustomAxisDemo {
|
|||||||
|
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
fn ui(&mut self, ui: &mut Ui) -> Response {
|
fn ui(&mut self, ui: &mut Ui) -> Response {
|
||||||
const MINS_PER_DAY: f64 = CustomAxisDemo::MINS_PER_DAY;
|
const MINS_PER_DAY: f64 = CustomAxesDemo::MINS_PER_DAY;
|
||||||
const MINS_PER_H: f64 = CustomAxisDemo::MINS_PER_H;
|
const MINS_PER_H: f64 = CustomAxesDemo::MINS_PER_H;
|
||||||
|
|
||||||
fn day(x: f64) -> f64 {
|
fn day(x: f64) -> f64 {
|
||||||
(x / MINS_PER_DAY).floor()
|
(x / MINS_PER_DAY).floor()
|
||||||
@@ -561,10 +561,10 @@ impl CustomAxisDemo {
|
|||||||
.data_aspect(2.0 * MINS_PER_DAY as f32)
|
.data_aspect(2.0 * MINS_PER_DAY as f32)
|
||||||
.x_axis_formatter(x_fmt)
|
.x_axis_formatter(x_fmt)
|
||||||
.y_axis_formatter(y_fmt)
|
.y_axis_formatter(y_fmt)
|
||||||
.x_grid_spacer(CustomAxisDemo::x_grid)
|
.x_grid_spacer(CustomAxesDemo::x_grid)
|
||||||
.label_formatter(label_fmt)
|
.label_formatter(label_fmt)
|
||||||
.show(ui, |plot_ui| {
|
.show(ui, |plot_ui| {
|
||||||
plot_ui.line(CustomAxisDemo::logistic_fn());
|
plot_ui.line(CustomAxesDemo::logistic_fn());
|
||||||
})
|
})
|
||||||
.response
|
.response
|
||||||
}
|
}
|
||||||
@@ -573,14 +573,14 @@ impl CustomAxisDemo {
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
struct LinkedAxisDemo {
|
struct LinkedAxesDemo {
|
||||||
link_x: bool,
|
link_x: bool,
|
||||||
link_y: bool,
|
link_y: bool,
|
||||||
link_cursor_x: bool,
|
link_cursor_x: bool,
|
||||||
link_cursor_y: bool,
|
link_cursor_y: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LinkedAxisDemo {
|
impl Default for LinkedAxesDemo {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let link_x = true;
|
let link_x = true;
|
||||||
let link_y = false;
|
let link_y = false;
|
||||||
@@ -595,7 +595,7 @@ impl Default for LinkedAxisDemo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LinkedAxisDemo {
|
impl LinkedAxesDemo {
|
||||||
fn line_with_slope(slope: f64) -> Line {
|
fn line_with_slope(slope: f64) -> Line {
|
||||||
Line::new(PlotPoints::from_explicit_callback(
|
Line::new(PlotPoints::from_explicit_callback(
|
||||||
move |x| slope * x,
|
move |x| slope * x,
|
||||||
@@ -621,11 +621,11 @@ impl LinkedAxisDemo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn configure_plot(plot_ui: &mut plot::PlotUi) {
|
fn configure_plot(plot_ui: &mut plot::PlotUi) {
|
||||||
plot_ui.line(LinkedAxisDemo::line_with_slope(0.5));
|
plot_ui.line(LinkedAxesDemo::line_with_slope(0.5));
|
||||||
plot_ui.line(LinkedAxisDemo::line_with_slope(1.0));
|
plot_ui.line(LinkedAxesDemo::line_with_slope(1.0));
|
||||||
plot_ui.line(LinkedAxisDemo::line_with_slope(2.0));
|
plot_ui.line(LinkedAxesDemo::line_with_slope(2.0));
|
||||||
plot_ui.line(LinkedAxisDemo::sin());
|
plot_ui.line(LinkedAxesDemo::sin());
|
||||||
plot_ui.line(LinkedAxisDemo::cos());
|
plot_ui.line(LinkedAxesDemo::cos());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui(&mut self, ui: &mut Ui) -> Response {
|
fn ui(&mut self, ui: &mut Ui) -> Response {
|
||||||
@@ -648,14 +648,14 @@ impl LinkedAxisDemo {
|
|||||||
.height(250.0)
|
.height(250.0)
|
||||||
.link_axis(link_group_id, self.link_x, self.link_y)
|
.link_axis(link_group_id, self.link_x, self.link_y)
|
||||||
.link_cursor(link_group_id, self.link_cursor_x, self.link_cursor_y)
|
.link_cursor(link_group_id, self.link_cursor_x, self.link_cursor_y)
|
||||||
.show(ui, LinkedAxisDemo::configure_plot);
|
.show(ui, LinkedAxesDemo::configure_plot);
|
||||||
Plot::new("linked_axis_2")
|
Plot::new("linked_axis_2")
|
||||||
.data_aspect(2.0)
|
.data_aspect(2.0)
|
||||||
.width(150.0)
|
.width(150.0)
|
||||||
.height(250.0)
|
.height(250.0)
|
||||||
.link_axis(link_group_id, self.link_x, self.link_y)
|
.link_axis(link_group_id, self.link_x, self.link_y)
|
||||||
.link_cursor(link_group_id, self.link_cursor_x, self.link_cursor_y)
|
.link_cursor(link_group_id, self.link_cursor_x, self.link_cursor_y)
|
||||||
.show(ui, LinkedAxisDemo::configure_plot);
|
.show(ui, LinkedAxesDemo::configure_plot);
|
||||||
});
|
});
|
||||||
Plot::new("linked_axis_3")
|
Plot::new("linked_axis_3")
|
||||||
.data_aspect(0.5)
|
.data_aspect(0.5)
|
||||||
@@ -663,7 +663,7 @@ impl LinkedAxisDemo {
|
|||||||
.height(150.0)
|
.height(150.0)
|
||||||
.link_axis(link_group_id, self.link_x, self.link_y)
|
.link_axis(link_group_id, self.link_x, self.link_y)
|
||||||
.link_cursor(link_group_id, self.link_cursor_x, self.link_cursor_y)
|
.link_cursor(link_group_id, self.link_cursor_x, self.link_cursor_y)
|
||||||
.show(ui, LinkedAxisDemo::configure_plot)
|
.show(ui, LinkedAxesDemo::configure_plot)
|
||||||
.response
|
.response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -761,7 +761,7 @@ impl InteractionDemo {
|
|||||||
plot_ui.pointer_coordinate(),
|
plot_ui.pointer_coordinate(),
|
||||||
plot_ui.pointer_coordinate_drag_delta(),
|
plot_ui.pointer_coordinate_drag_delta(),
|
||||||
plot_ui.plot_bounds(),
|
plot_ui.plot_bounds(),
|
||||||
plot_ui.plot_hovered(),
|
plot_ui.response().hovered(),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -774,21 +774,18 @@ impl InteractionDemo {
|
|||||||
"origin in screen coordinates: x: {:.02}, y: {:.02}",
|
"origin in screen coordinates: x: {:.02}, y: {:.02}",
|
||||||
screen_pos.x, screen_pos.y
|
screen_pos.x, screen_pos.y
|
||||||
));
|
));
|
||||||
ui.label(format!("plot hovered: {}", hovered));
|
ui.label(format!("plot hovered: {hovered}"));
|
||||||
let coordinate_text = if let Some(coordinate) = pointer_coordinate {
|
let coordinate_text = if let Some(coordinate) = pointer_coordinate {
|
||||||
format!("x: {:.02}, y: {:.02}", coordinate.x, coordinate.y)
|
format!("x: {:.02}, y: {:.02}", coordinate.x, coordinate.y)
|
||||||
} else {
|
} else {
|
||||||
"None".to_owned()
|
"None".to_owned()
|
||||||
};
|
};
|
||||||
ui.label(format!("pointer coordinate: {}", coordinate_text));
|
ui.label(format!("pointer coordinate: {coordinate_text}"));
|
||||||
let coordinate_text = format!(
|
let coordinate_text = format!(
|
||||||
"x: {:.02}, y: {:.02}",
|
"x: {:.02}, y: {:.02}",
|
||||||
pointer_coordinate_drag_delta.x, pointer_coordinate_drag_delta.y
|
pointer_coordinate_drag_delta.x, pointer_coordinate_drag_delta.y
|
||||||
);
|
);
|
||||||
ui.label(format!(
|
ui.label(format!("pointer coordinate drag delta: {coordinate_text}"));
|
||||||
"pointer coordinate drag delta: {}",
|
|
||||||
coordinate_text
|
|
||||||
));
|
|
||||||
|
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,10 +232,10 @@ impl super::View for ScrollTo {
|
|||||||
for item in 1..=50 {
|
for item in 1..=50 {
|
||||||
if track_item && item == self.track_item {
|
if track_item && item == self.track_item {
|
||||||
let response =
|
let response =
|
||||||
ui.colored_label(Color32::YELLOW, format!("This is item {}", item));
|
ui.colored_label(Color32::YELLOW, format!("This is item {item}"));
|
||||||
response.scroll_to_me(self.tack_item_align);
|
response.scroll_to_me(self.tack_item_align);
|
||||||
} else {
|
} else {
|
||||||
ui.label(format!("This is item {}", item));
|
ui.label(format!("This is item {item}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -254,8 +254,7 @@ impl super::View for ScrollTo {
|
|||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"Scroll offset: {:.0}/{:.0} px",
|
"Scroll offset: {current_scroll:.0}/{max_scroll:.0} px"
|
||||||
current_scroll, max_scroll
|
|
||||||
));
|
));
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ impl super::View for StripDemo {
|
|||||||
.size(Size::exact(50.0))
|
.size(Size::exact(50.0))
|
||||||
.size(Size::remainder())
|
.size(Size::remainder())
|
||||||
.size(Size::relative(0.5).at_least(60.0))
|
.size(Size::relative(0.5).at_least(60.0))
|
||||||
.size(Size::exact(10.0))
|
.size(Size::exact(10.5))
|
||||||
.vertical(|mut strip| {
|
.vertical(|mut strip| {
|
||||||
strip.cell(|ui| {
|
strip.cell(|ui| {
|
||||||
ui.painter().rect_filled(
|
ui.painter().rect_filled(
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ impl super::Demo for TableDemo {
|
|||||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||||
egui::Window::new(self.name())
|
egui::Window::new(self.name())
|
||||||
.open(open)
|
.open(open)
|
||||||
.resizable(true)
|
|
||||||
.default_width(400.0)
|
.default_width(400.0)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
use super::View as _;
|
use super::View as _;
|
||||||
@@ -102,7 +101,7 @@ impl super::View for TableDemo {
|
|||||||
use egui_extras::{Size, StripBuilder};
|
use egui_extras::{Size, StripBuilder};
|
||||||
StripBuilder::new(ui)
|
StripBuilder::new(ui)
|
||||||
.size(Size::remainder().at_least(100.0)) // for the table
|
.size(Size::remainder().at_least(100.0)) // for the table
|
||||||
.size(Size::exact(10.0)) // for the source code link
|
.size(Size::exact(10.5)) // for the source code link
|
||||||
.vertical(|mut strip| {
|
.vertical(|mut strip| {
|
||||||
strip.cell(|ui| {
|
strip.cell(|ui| {
|
||||||
egui::ScrollArea::horizontal().show(ui, |ui| {
|
egui::ScrollArea::horizontal().show(ui, |ui| {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ impl super::View for CursorTest {
|
|||||||
ui.heading("Hover to switch cursor icon:");
|
ui.heading("Hover to switch cursor icon:");
|
||||||
for &cursor_icon in &egui::CursorIcon::ALL {
|
for &cursor_icon in &egui::CursorIcon::ALL {
|
||||||
let _ = ui
|
let _ = ui
|
||||||
.button(format!("{:?}", cursor_icon))
|
.button(format!("{cursor_icon:?}"))
|
||||||
.on_hover_cursor(cursor_icon);
|
.on_hover_cursor(cursor_icon);
|
||||||
}
|
}
|
||||||
ui.add(crate::egui_github_link_file!());
|
ui.add(crate::egui_github_link_file!());
|
||||||
@@ -239,7 +239,7 @@ impl super::View for TableTest {
|
|||||||
for row in 0..self.num_rows {
|
for row in 0..self.num_rows {
|
||||||
for col in 0..self.num_cols {
|
for col in 0..self.num_cols {
|
||||||
if col == 0 {
|
if col == 0 {
|
||||||
ui.label(format!("row {}", row));
|
ui.label(format!("row {row}"));
|
||||||
} else {
|
} else {
|
||||||
let word_idx = row * 3 + col * 5;
|
let word_idx = row * 3 + col * 5;
|
||||||
let word_count = (row * 5 + col * 75) % 13;
|
let word_count = (row * 5 + col * 75) % 13;
|
||||||
@@ -350,13 +350,13 @@ impl super::View for InputTest {
|
|||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
|
|
||||||
if response.clicked_by(button) {
|
if response.clicked_by(button) {
|
||||||
writeln!(new_info, "Clicked by {:?} button", button).ok();
|
writeln!(new_info, "Clicked by {button:?} button").ok();
|
||||||
}
|
}
|
||||||
if response.double_clicked_by(button) {
|
if response.double_clicked_by(button) {
|
||||||
writeln!(new_info, "Double-clicked by {:?} button", button).ok();
|
writeln!(new_info, "Double-clicked by {button:?} button").ok();
|
||||||
}
|
}
|
||||||
if response.triple_clicked_by(button) {
|
if response.triple_clicked_by(button) {
|
||||||
writeln!(new_info, "Triple-clicked by {:?} button", button).ok();
|
writeln!(new_info, "Triple-clicked by {button:?} button").ok();
|
||||||
}
|
}
|
||||||
if response.dragged_by(button) {
|
if response.dragged_by(button) {
|
||||||
writeln!(
|
writeln!(
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ impl WidgetGallery {
|
|||||||
ui.add(doc_link_label("Hyperlink", "Hyperlink"));
|
ui.add(doc_link_label("Hyperlink", "Hyperlink"));
|
||||||
use egui::special_emojis::GITHUB;
|
use egui::special_emojis::GITHUB;
|
||||||
ui.hyperlink_to(
|
ui.hyperlink_to(
|
||||||
format!("{} egui on GitHub", GITHUB),
|
format!("{GITHUB} egui on GitHub"),
|
||||||
"https://github.com/emilk/egui",
|
"https://github.com/emilk/egui",
|
||||||
);
|
);
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
@@ -173,7 +173,7 @@ impl WidgetGallery {
|
|||||||
ui.add(doc_link_label("ComboBox", "ComboBox"));
|
ui.add(doc_link_label("ComboBox", "ComboBox"));
|
||||||
|
|
||||||
egui::ComboBox::from_label("Take your pick")
|
egui::ComboBox::from_label("Take your pick")
|
||||||
.selected_text(format!("{:?}", radio))
|
.selected_text(format!("{radio:?}"))
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
ui.style_mut().wrap = Some(false);
|
ui.style_mut().wrap = Some(false);
|
||||||
ui.set_min_width(60.0);
|
ui.set_min_width(60.0);
|
||||||
@@ -277,8 +277,8 @@ fn example_plot(ui: &mut egui::Ui) -> egui::Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn doc_link_label<'a>(title: &'a str, search_term: &'a str) -> impl egui::Widget + 'a {
|
fn doc_link_label<'a>(title: &'a str, search_term: &'a str) -> impl egui::Widget + 'a {
|
||||||
let label = format!("{}:", title);
|
let label = format!("{title}:");
|
||||||
let url = format!("https://docs.rs/egui?search={}", search_term);
|
let url = format!("https://docs.rs/egui?search={search_term}");
|
||||||
move |ui: &mut egui::Ui| {
|
move |ui: &mut egui::Ui| {
|
||||||
ui.hyperlink_to(label, url).on_hover_ui(|ui| {
|
ui.hyperlink_to(label, url).on_hover_ui(|ui| {
|
||||||
ui.horizontal_wrapped(|ui| {
|
ui.horizontal_wrapped(|ui| {
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ fn numbered_point(ui: &mut Ui, width: f32, number: &str) -> Response {
|
|||||||
let font_id = TextStyle::Body.resolve(ui.style());
|
let font_id = TextStyle::Body.resolve(ui.style());
|
||||||
let row_height = ui.fonts(|f| f.row_height(&font_id));
|
let row_height = ui.fonts(|f| f.row_height(&font_id));
|
||||||
let (rect, response) = ui.allocate_exact_size(vec2(width, row_height), Sense::hover());
|
let (rect, response) = ui.allocate_exact_size(vec2(width, row_height), Sense::hover());
|
||||||
let text = format!("{}.", number);
|
let text = format!("{number}.");
|
||||||
let text_color = ui.visuals().strong_text_color();
|
let text_color = ui.visuals().strong_text_color();
|
||||||
ui.painter().text(
|
ui.painter().text(
|
||||||
rect.right_center(),
|
rect.right_center(),
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# Changelog for egui_extras
|
# Changelog for egui_extras
|
||||||
All notable changes to the `egui_extras` integration will be noted in this file.
|
All notable changes to the `egui_extras` integration will be noted in this file.
|
||||||
|
|
||||||
|
This file is updated upon each release.
|
||||||
## Unreleased
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
## 0.22.0 - 2023-05-23
|
## 0.22.0 - 2023-05-23
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ authors = [
|
|||||||
]
|
]
|
||||||
description = "Extra functionality and widgets for the egui GUI library"
|
description = "Extra functionality and widgets for the egui GUI library"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.67"
|
||||||
homepage = "https://github.com/emilk/egui"
|
homepage = "https://github.com/emilk/egui"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -428,6 +428,6 @@ fn month_name(i: u32) -> &'static str {
|
|||||||
10 => "October",
|
10 => "October",
|
||||||
11 => "November",
|
11 => "November",
|
||||||
12 => "December",
|
12 => "December",
|
||||||
_ => panic!("Unknown month: {}", i),
|
_ => panic!("Unknown month: {i}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,15 @@ pub use usvg::FitTo;
|
|||||||
/// Use the `svg` and `image` features to enable more constructors.
|
/// Use the `svg` and `image` features to enable more constructors.
|
||||||
pub struct RetainedImage {
|
pub struct RetainedImage {
|
||||||
debug_name: String,
|
debug_name: String,
|
||||||
|
|
||||||
size: [usize; 2],
|
size: [usize; 2],
|
||||||
|
|
||||||
/// Cleared once [`Self::texture`] has been loaded.
|
/// Cleared once [`Self::texture`] has been loaded.
|
||||||
image: Mutex<egui::ColorImage>,
|
image: Mutex<egui::ColorImage>,
|
||||||
|
|
||||||
/// Lazily loaded when we have an egui context.
|
/// Lazily loaded when we have an egui context.
|
||||||
texture: Mutex<Option<egui::TextureHandle>>,
|
texture: Mutex<Option<egui::TextureHandle>>,
|
||||||
|
|
||||||
options: TextureOptions,
|
options: TextureOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,7 +258,7 @@ pub fn load_svg_bytes_with_size(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut pixmap = tiny_skia::Pixmap::new(w, h)
|
let mut pixmap = tiny_skia::Pixmap::new(w, h)
|
||||||
.ok_or_else(|| format!("Failed to create SVG Pixmap of size {}x{}", w, h))?;
|
.ok_or_else(|| format!("Failed to create SVG Pixmap of size {w}x{h}"))?;
|
||||||
|
|
||||||
resvg::render(&rtree, fit_to, Default::default(), pixmap.as_mut())
|
resvg::render(&rtree, fit_to, Default::default(), pixmap.as_mut())
|
||||||
.ok_or_else(|| "Failed to render SVG".to_owned())?;
|
.ok_or_else(|| "Failed to render SVG".to_owned())?;
|
||||||
|
|||||||
@@ -32,9 +32,11 @@ pub struct StripLayout<'l> {
|
|||||||
direction: CellDirection,
|
direction: CellDirection,
|
||||||
pub(crate) rect: Rect,
|
pub(crate) rect: Rect,
|
||||||
pub(crate) cursor: Pos2,
|
pub(crate) cursor: Pos2,
|
||||||
|
|
||||||
/// Keeps track of the max used position,
|
/// Keeps track of the max used position,
|
||||||
/// so we know how much space we used.
|
/// so we know how much space we used.
|
||||||
max: Pos2,
|
max: Pos2,
|
||||||
|
|
||||||
cell_layout: egui::Layout,
|
cell_layout: egui::Layout,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
|
use egui::Rangef;
|
||||||
|
|
||||||
/// Size hint for table column/strip cell.
|
/// Size hint for table column/strip cell.
|
||||||
#[derive(Clone, Debug, Copy)]
|
#[derive(Clone, Debug, Copy)]
|
||||||
pub enum Size {
|
pub enum Size {
|
||||||
/// Absolute size in points, with a given range of allowed sizes to resize within.
|
/// Absolute size in points, with a given range of allowed sizes to resize within.
|
||||||
Absolute { initial: f32, range: (f32, f32) },
|
Absolute { initial: f32, range: Rangef },
|
||||||
|
|
||||||
/// Relative size relative to all available space.
|
/// Relative size relative to all available space.
|
||||||
Relative { fraction: f32, range: (f32, f32) },
|
Relative { fraction: f32, range: Rangef },
|
||||||
|
|
||||||
/// Multiple remainders each get the same space.
|
/// Multiple remainders each get the same space.
|
||||||
Remainder { range: (f32, f32) },
|
Remainder { range: Rangef },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Size {
|
impl Size {
|
||||||
@@ -16,7 +18,7 @@ impl Size {
|
|||||||
pub fn exact(points: f32) -> Self {
|
pub fn exact(points: f32) -> Self {
|
||||||
Self::Absolute {
|
Self::Absolute {
|
||||||
initial: points,
|
initial: points,
|
||||||
range: (points, points),
|
range: Rangef::new(points, points),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +26,7 @@ impl Size {
|
|||||||
pub fn initial(points: f32) -> Self {
|
pub fn initial(points: f32) -> Self {
|
||||||
Self::Absolute {
|
Self::Absolute {
|
||||||
initial: points,
|
initial: points,
|
||||||
range: (0.0, f32::INFINITY),
|
range: Rangef::new(0.0, f32::INFINITY),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,14 +35,14 @@ impl Size {
|
|||||||
egui::egui_assert!(0.0 <= fraction && fraction <= 1.0);
|
egui::egui_assert!(0.0 <= fraction && fraction <= 1.0);
|
||||||
Self::Relative {
|
Self::Relative {
|
||||||
fraction,
|
fraction,
|
||||||
range: (0.0, f32::INFINITY),
|
range: Rangef::new(0.0, f32::INFINITY),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Multiple remainders each get the same space.
|
/// Multiple remainders each get the same space.
|
||||||
pub fn remainder() -> Self {
|
pub fn remainder() -> Self {
|
||||||
Self::Remainder {
|
Self::Remainder {
|
||||||
range: (0.0, f32::INFINITY),
|
range: Rangef::new(0.0, f32::INFINITY),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +52,7 @@ impl Size {
|
|||||||
Self::Absolute { range, .. }
|
Self::Absolute { range, .. }
|
||||||
| Self::Relative { range, .. }
|
| Self::Relative { range, .. }
|
||||||
| Self::Remainder { range, .. } => {
|
| Self::Remainder { range, .. } => {
|
||||||
range.0 = minimum;
|
range.min = minimum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
@@ -62,14 +64,14 @@ impl Size {
|
|||||||
Self::Absolute { range, .. }
|
Self::Absolute { range, .. }
|
||||||
| Self::Relative { range, .. }
|
| Self::Relative { range, .. }
|
||||||
| Self::Remainder { range, .. } => {
|
| Self::Remainder { range, .. } => {
|
||||||
range.1 = maximum;
|
range.max = maximum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allowed range of movement (in points), if in a resizable [`Table`](crate::table::Table).
|
/// Allowed range of movement (in points), if in a resizable [`Table`](crate::table::Table).
|
||||||
pub fn range(self) -> (f32, f32) {
|
pub fn range(self) -> Rangef {
|
||||||
match self {
|
match self {
|
||||||
Self::Absolute { range, .. }
|
Self::Absolute { range, .. }
|
||||||
| Self::Relative { range, .. }
|
| Self::Relative { range, .. }
|
||||||
@@ -99,12 +101,9 @@ impl Sizing {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|&size| match size {
|
.map(|&size| match size {
|
||||||
Size::Absolute { initial, .. } => initial,
|
Size::Absolute { initial, .. } => initial,
|
||||||
Size::Relative {
|
Size::Relative { fraction, range } => {
|
||||||
fraction,
|
|
||||||
range: (min, max),
|
|
||||||
} => {
|
|
||||||
assert!(0.0 <= fraction && fraction <= 1.0);
|
assert!(0.0 <= fraction && fraction <= 1.0);
|
||||||
(length * fraction).clamp(min, max)
|
range.clamp(length * fraction)
|
||||||
}
|
}
|
||||||
Size::Remainder { .. } => {
|
Size::Remainder { .. } => {
|
||||||
remainders += 1;
|
remainders += 1;
|
||||||
@@ -120,9 +119,9 @@ impl Sizing {
|
|||||||
let mut remainder_length = length - sum_non_remainder;
|
let mut remainder_length = length - sum_non_remainder;
|
||||||
let avg_remainder_length = 0.0f32.max(remainder_length / remainders as f32).floor();
|
let avg_remainder_length = 0.0f32.max(remainder_length / remainders as f32).floor();
|
||||||
self.sizes.iter().for_each(|&size| {
|
self.sizes.iter().for_each(|&size| {
|
||||||
if let Size::Remainder { range: (min, _max) } = size {
|
if let Size::Remainder { range } = size {
|
||||||
if avg_remainder_length < min {
|
if avg_remainder_length < range.min {
|
||||||
remainder_length -= min;
|
remainder_length -= range.min;
|
||||||
remainders -= 1;
|
remainders -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,11 +137,8 @@ impl Sizing {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|&size| match size {
|
.map(|&size| match size {
|
||||||
Size::Absolute { initial, .. } => initial,
|
Size::Absolute { initial, .. } => initial,
|
||||||
Size::Relative {
|
Size::Relative { fraction, range } => range.clamp(length * fraction),
|
||||||
fraction,
|
Size::Remainder { range } => range.clamp(avg_remainder_length),
|
||||||
range: (min, max),
|
|
||||||
} => (length * fraction).clamp(min, max),
|
|
||||||
Size::Remainder { range: (min, max) } => avg_remainder_length.clamp(min, max),
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,13 +72,13 @@ impl<'a> StripBuilder<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate space for for one column/row.
|
/// Allocate space for one column/row.
|
||||||
pub fn size(mut self, size: Size) -> Self {
|
pub fn size(mut self, size: Size) -> Self {
|
||||||
self.sizing.add(size);
|
self.sizing.add(size);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate space for for several columns/rows at once.
|
/// Allocate space for several columns/rows at once.
|
||||||
pub fn sizes(mut self, size: Size, count: usize) -> Self {
|
pub fn sizes(mut self, size: Size, count: usize) -> Self {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
self.sizing.add(size);
|
self.sizing.add(size);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//! | fixed size | all available space/minimum | 30% of available width | fixed size |
|
//! | fixed size | all available space/minimum | 30% of available width | fixed size |
|
||||||
//! Takes all available height, so if you want something below the table, put it in a strip.
|
//! Takes all available height, so if you want something below the table, put it in a strip.
|
||||||
|
|
||||||
use egui::{Align, NumExt as _, Rect, Response, ScrollArea, Ui, Vec2};
|
use egui::{Align, NumExt as _, Rangef, Rect, Response, ScrollArea, Ui, Vec2};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
layout::{CellDirection, CellSize},
|
layout::{CellDirection, CellSize},
|
||||||
@@ -28,7 +28,9 @@ enum InitialColumnSize {
|
|||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct Column {
|
pub struct Column {
|
||||||
initial_width: InitialColumnSize,
|
initial_width: InitialColumnSize,
|
||||||
width_range: (f32, f32),
|
|
||||||
|
width_range: Rangef,
|
||||||
|
|
||||||
/// Clip contents if too narrow?
|
/// Clip contents if too narrow?
|
||||||
clip: bool,
|
clip: bool,
|
||||||
|
|
||||||
@@ -78,7 +80,7 @@ impl Column {
|
|||||||
fn new(initial_width: InitialColumnSize) -> Self {
|
fn new(initial_width: InitialColumnSize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
initial_width,
|
initial_width,
|
||||||
width_range: (0.0, f32::INFINITY),
|
width_range: Rangef::new(0.0, f32::INFINITY),
|
||||||
resizable: None,
|
resizable: None,
|
||||||
clip: false,
|
clip: false,
|
||||||
}
|
}
|
||||||
@@ -110,7 +112,7 @@ impl Column {
|
|||||||
///
|
///
|
||||||
/// Default: 0.0
|
/// Default: 0.0
|
||||||
pub fn at_least(mut self, minimum: f32) -> Self {
|
pub fn at_least(mut self, minimum: f32) -> Self {
|
||||||
self.width_range.0 = minimum;
|
self.width_range.min = minimum;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,13 +120,13 @@ impl Column {
|
|||||||
///
|
///
|
||||||
/// Default: [`f32::INFINITY`]
|
/// Default: [`f32::INFINITY`]
|
||||||
pub fn at_most(mut self, maximum: f32) -> Self {
|
pub fn at_most(mut self, maximum: f32) -> Self {
|
||||||
self.width_range.1 = maximum;
|
self.width_range.max = maximum;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allowed range of movement (in points), if in a resizable [`Table`](crate::table::Table).
|
/// Allowed range of movement (in points), if in a resizable [`Table`](crate::table::Table).
|
||||||
pub fn range(mut self, range: std::ops::RangeInclusive<f32>) -> Self {
|
pub fn range(mut self, range: impl Into<Rangef>) -> Self {
|
||||||
self.width_range = (*range.start(), *range.end());
|
self.width_range = range.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,8 +148,8 @@ fn to_sizing(columns: &[Column]) -> crate::sizing::Sizing {
|
|||||||
InitialColumnSize::Automatic(suggested_width) => Size::initial(suggested_width),
|
InitialColumnSize::Automatic(suggested_width) => Size::initial(suggested_width),
|
||||||
InitialColumnSize::Remainder => Size::remainder(),
|
InitialColumnSize::Remainder => Size::remainder(),
|
||||||
}
|
}
|
||||||
.at_least(column.width_range.0)
|
.at_least(column.width_range.min)
|
||||||
.at_most(column.width_range.1);
|
.at_most(column.width_range.max);
|
||||||
sizing.add(size);
|
sizing.add(size);
|
||||||
}
|
}
|
||||||
sizing
|
sizing
|
||||||
@@ -511,8 +513,10 @@ pub struct Table<'a> {
|
|||||||
columns: Vec<Column>,
|
columns: Vec<Column>,
|
||||||
available_width: f32,
|
available_width: f32,
|
||||||
state: TableState,
|
state: TableState,
|
||||||
|
|
||||||
/// Accumulated maximum used widths for each column.
|
/// Accumulated maximum used widths for each column.
|
||||||
max_used_widths: Vec<f32>,
|
max_used_widths: Vec<f32>,
|
||||||
|
|
||||||
first_frame_auto_size_columns: bool,
|
first_frame_auto_size_columns: bool,
|
||||||
resizable: bool,
|
resizable: bool,
|
||||||
striped: bool,
|
striped: bool,
|
||||||
@@ -598,13 +602,13 @@ impl<'a> Table<'a> {
|
|||||||
|
|
||||||
if scroll_to_row.is_some() && scroll_to_y_range.is_none() {
|
if scroll_to_row.is_some() && scroll_to_y_range.is_none() {
|
||||||
// TableBody::row didn't find the right row, so scroll to the bottom:
|
// TableBody::row didn't find the right row, so scroll to the bottom:
|
||||||
scroll_to_y_range = Some((f32::INFINITY, f32::INFINITY));
|
scroll_to_y_range = Some(Rangef::new(f32::INFINITY, f32::INFINITY));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some((min_y, max_y)) = scroll_to_y_range {
|
if let Some(y_range) = scroll_to_y_range {
|
||||||
let x = 0.0; // ignored, we only have vertical scrolling
|
let x = 0.0; // ignored, we only have vertical scrolling
|
||||||
let rect = egui::Rect::from_min_max(egui::pos2(x, min_y), egui::pos2(x, max_y));
|
let rect = egui::Rect::from_x_y_ranges(x..=x, y_range);
|
||||||
let align = scroll_to_row.and_then(|(_, a)| a);
|
let align = scroll_to_row.and_then(|(_, a)| a);
|
||||||
ui.scroll_to_rect(rect, align);
|
ui.scroll_to_rect(rect, align);
|
||||||
}
|
}
|
||||||
@@ -617,14 +621,14 @@ impl<'a> Table<'a> {
|
|||||||
for (i, column_width) in state.column_widths.iter_mut().enumerate() {
|
for (i, column_width) in state.column_widths.iter_mut().enumerate() {
|
||||||
let column = &columns[i];
|
let column = &columns[i];
|
||||||
let column_is_resizable = column.resizable.unwrap_or(resizable);
|
let column_is_resizable = column.resizable.unwrap_or(resizable);
|
||||||
let (min_width, max_width) = column.width_range;
|
let width_range = column.width_range;
|
||||||
|
|
||||||
if !column.clip {
|
if !column.clip {
|
||||||
// Unless we clip we don't want to shrink below the
|
// Unless we clip we don't want to shrink below the
|
||||||
// size that was actually used:
|
// size that was actually used:
|
||||||
*column_width = column_width.at_least(max_used_widths[i]);
|
*column_width = column_width.at_least(max_used_widths[i]);
|
||||||
}
|
}
|
||||||
*column_width = column_width.clamp(min_width, max_width);
|
*column_width = width_range.clamp(*column_width);
|
||||||
|
|
||||||
let is_last_column = i + 1 == columns.len();
|
let is_last_column = i + 1 == columns.len();
|
||||||
|
|
||||||
@@ -633,7 +637,7 @@ impl<'a> Table<'a> {
|
|||||||
let eps = 0.1; // just to avoid some rounding errors.
|
let eps = 0.1; // just to avoid some rounding errors.
|
||||||
*column_width = available_width - eps;
|
*column_width = available_width - eps;
|
||||||
*column_width = column_width.at_least(max_used_widths[i]);
|
*column_width = column_width.at_least(max_used_widths[i]);
|
||||||
*column_width = column_width.clamp(min_width, max_width);
|
*column_width = width_range.clamp(*column_width);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,7 +645,7 @@ impl<'a> Table<'a> {
|
|||||||
|
|
||||||
if column.is_auto() && (first_frame_auto_size_columns || !column_is_resizable) {
|
if column.is_auto() && (first_frame_auto_size_columns || !column_is_resizable) {
|
||||||
*column_width = max_used_widths[i];
|
*column_width = max_used_widths[i];
|
||||||
*column_width = column_width.clamp(min_width, max_width);
|
*column_width = width_range.clamp(*column_width);
|
||||||
} else if column_is_resizable {
|
} else if column_is_resizable {
|
||||||
let column_resize_id = ui.id().with("resize_column").with(i);
|
let column_resize_id = ui.id().with("resize_column").with(i);
|
||||||
|
|
||||||
@@ -656,7 +660,7 @@ impl<'a> Table<'a> {
|
|||||||
if resize_response.double_clicked() {
|
if resize_response.double_clicked() {
|
||||||
// Resize to the minimum of what is needed.
|
// Resize to the minimum of what is needed.
|
||||||
|
|
||||||
*column_width = max_used_widths[i].clamp(min_width, max_width);
|
*column_width = width_range.clamp(max_used_widths[i]);
|
||||||
} else if resize_response.dragged() {
|
} else if resize_response.dragged() {
|
||||||
if let Some(pointer) = ui.ctx().pointer_latest_pos() {
|
if let Some(pointer) = ui.ctx().pointer_latest_pos() {
|
||||||
let mut new_width = *column_width + pointer.x - x;
|
let mut new_width = *column_width + pointer.x - x;
|
||||||
@@ -671,7 +675,7 @@ impl<'a> Table<'a> {
|
|||||||
new_width =
|
new_width =
|
||||||
new_width.at_least(max_used_widths[i] - max_shrinkage_per_frame);
|
new_width.at_least(max_used_widths[i] - max_shrinkage_per_frame);
|
||||||
}
|
}
|
||||||
new_width = new_width.clamp(min_width, max_width);
|
new_width = width_range.clamp(new_width);
|
||||||
|
|
||||||
let x = x - *column_width + new_width;
|
let x = x - *column_width + new_width;
|
||||||
(p0.x, p1.x) = (x, x);
|
(p0.x, p1.x) = (x, x);
|
||||||
@@ -731,7 +735,7 @@ pub struct TableBody<'a> {
|
|||||||
|
|
||||||
/// If we find the correct row to scroll to,
|
/// If we find the correct row to scroll to,
|
||||||
/// this is set to the y-range of the row.
|
/// this is set to the y-range of the row.
|
||||||
scroll_to_y_range: &'a mut Option<(f32, f32)>,
|
scroll_to_y_range: &'a mut Option<Rangef>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TableBody<'a> {
|
impl<'a> TableBody<'a> {
|
||||||
@@ -779,7 +783,7 @@ impl<'a> TableBody<'a> {
|
|||||||
let bottom_y = self.layout.cursor.y;
|
let bottom_y = self.layout.cursor.y;
|
||||||
|
|
||||||
if Some(self.row_nr) == self.scroll_to_row {
|
if Some(self.row_nr) == self.scroll_to_row {
|
||||||
*self.scroll_to_y_range = Some((top_y, bottom_y));
|
*self.scroll_to_y_range = Some(Rangef::new(top_y, bottom_y));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.row_nr += 1;
|
self.row_nr += 1;
|
||||||
@@ -819,7 +823,7 @@ impl<'a> TableBody<'a> {
|
|||||||
|
|
||||||
if let Some(scroll_to_row) = self.scroll_to_row {
|
if let Some(scroll_to_row) = self.scroll_to_row {
|
||||||
let scroll_to_row = scroll_to_row.at_most(total_rows.saturating_sub(1)) as f32;
|
let scroll_to_row = scroll_to_row.at_most(total_rows.saturating_sub(1)) as f32;
|
||||||
*self.scroll_to_y_range = Some((
|
*self.scroll_to_y_range = Some(Rangef::new(
|
||||||
self.layout.cursor.y + scroll_to_row * row_height_with_spacing,
|
self.layout.cursor.y + scroll_to_row * row_height_with_spacing,
|
||||||
self.layout.cursor.y + (scroll_to_row + 1.0) * row_height_with_spacing,
|
self.layout.cursor.y + (scroll_to_row + 1.0) * row_height_with_spacing,
|
||||||
));
|
));
|
||||||
@@ -909,7 +913,7 @@ impl<'a> TableBody<'a> {
|
|||||||
cursor_y += (row_height + spacing.y) as f64;
|
cursor_y += (row_height + spacing.y) as f64;
|
||||||
|
|
||||||
if Some(row_index) == self.scroll_to_row {
|
if Some(row_index) == self.scroll_to_row {
|
||||||
*self.scroll_to_y_range = Some((
|
*self.scroll_to_y_range = Some(Rangef::new(
|
||||||
(scroll_to_y_range_offset + old_cursor_y) as f32,
|
(scroll_to_y_range_offset + old_cursor_y) as f32,
|
||||||
(scroll_to_y_range_offset + cursor_y) as f32,
|
(scroll_to_y_range_offset + cursor_y) as f32,
|
||||||
));
|
));
|
||||||
@@ -953,7 +957,7 @@ impl<'a> TableBody<'a> {
|
|||||||
cursor_y += (row_height + spacing.y) as f64;
|
cursor_y += (row_height + spacing.y) as f64;
|
||||||
|
|
||||||
if Some(row_index) == self.scroll_to_row {
|
if Some(row_index) == self.scroll_to_row {
|
||||||
*self.scroll_to_y_range = Some((
|
*self.scroll_to_y_range = Some(Rangef::new(
|
||||||
(scroll_to_y_range_offset + top_y) as f32,
|
(scroll_to_y_range_offset + top_y) as f32,
|
||||||
(scroll_to_y_range_offset + cursor_y) as f32,
|
(scroll_to_y_range_offset + cursor_y) as f32,
|
||||||
));
|
));
|
||||||
@@ -972,7 +976,7 @@ impl<'a> TableBody<'a> {
|
|||||||
let top_y = cursor_y;
|
let top_y = cursor_y;
|
||||||
cursor_y += (row_height + spacing.y) as f64;
|
cursor_y += (row_height + spacing.y) as f64;
|
||||||
if Some(row_index) == self.scroll_to_row {
|
if Some(row_index) == self.scroll_to_row {
|
||||||
*self.scroll_to_y_range = Some((
|
*self.scroll_to_y_range = Some(Rangef::new(
|
||||||
(scroll_to_y_range_offset + top_y) as f32,
|
(scroll_to_y_range_offset + top_y) as f32,
|
||||||
(scroll_to_y_range_offset + cursor_y) as f32,
|
(scroll_to_y_range_offset + cursor_y) as f32,
|
||||||
));
|
));
|
||||||
@@ -981,10 +985,8 @@ impl<'a> TableBody<'a> {
|
|||||||
|
|
||||||
if self.scroll_to_row.is_some() && self.scroll_to_y_range.is_none() {
|
if self.scroll_to_row.is_some() && self.scroll_to_y_range.is_none() {
|
||||||
// Catch desire to scroll past the end:
|
// Catch desire to scroll past the end:
|
||||||
*self.scroll_to_y_range = Some((
|
*self.scroll_to_y_range =
|
||||||
(scroll_to_y_range_offset + cursor_y) as f32,
|
Some(Rangef::point((scroll_to_y_range_offset + cursor_y) as f32));
|
||||||
(scroll_to_y_range_offset + cursor_y) as f32,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if height_below_visible > 0.0 {
|
if height_below_visible > 0.0 {
|
||||||
@@ -1013,8 +1015,10 @@ pub struct TableRow<'a, 'b> {
|
|||||||
layout: &'b mut StripLayout<'a>,
|
layout: &'b mut StripLayout<'a>,
|
||||||
columns: &'b [Column],
|
columns: &'b [Column],
|
||||||
widths: &'b [f32],
|
widths: &'b [f32],
|
||||||
|
|
||||||
/// grows during building with the maximum widths
|
/// grows during building with the maximum widths
|
||||||
max_used_widths: &'b mut [f32],
|
max_used_widths: &'b mut [f32],
|
||||||
|
|
||||||
col_index: usize,
|
col_index: usize,
|
||||||
striped: bool,
|
striped: bool,
|
||||||
height: f32,
|
height: f32,
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
# Changelog for egui_glium
|
# Changelog for egui_glium
|
||||||
All notable changes to the `egui_glium` integration will be noted in this file.
|
All notable changes to the `egui_glium` integration will be noted in this file.
|
||||||
|
|
||||||
|
This file is updated upon each release.
|
||||||
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
* Remove the `screen_reader` feature ([#2669](https://github.com/emilk/egui/pull/2669)).
|
* Remove the `screen_reader` feature ([#2669](https://github.com/emilk/egui/pull/2669)).
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ version = "0.22.0"
|
|||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "Bindings for using egui natively using the glium library"
|
description = "Bindings for using egui natively using the glium library"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.67"
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/egui_glium"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/egui_glium"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# Changelog for egui_glow
|
# Changelog for egui_glow
|
||||||
All notable changes to the `egui_glow` integration will be noted in this file.
|
All notable changes to the `egui_glow` integration will be noted in this file.
|
||||||
|
|
||||||
|
This file is updated upon each release.
|
||||||
## Unreleased
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
## 0.22.0 - 2023-05-23
|
## 0.22.0 - 2023-05-23
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ version = "0.22.0"
|
|||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "Bindings for using egui natively using the glow library"
|
description = "Bindings for using egui natively using the glow library"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.67"
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/egui_glow"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/egui_glow"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ version = "0.22.0"
|
|||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "Minimal 2D math library for GUI work"
|
description = "Minimal 2D math library for GUI work"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.67"
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/emath"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/emath"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -110,29 +110,25 @@ impl Align {
|
|||||||
/// assert_eq!(Max .align_size_within_range(INFINITY, NEG_INFINITY..=20.0), NEG_INFINITY..=20.0);
|
/// assert_eq!(Max .align_size_within_range(INFINITY, NEG_INFINITY..=20.0), NEG_INFINITY..=20.0);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn align_size_within_range(
|
pub fn align_size_within_range(self, size: f32, range: impl Into<Rangef>) -> Rangef {
|
||||||
self,
|
let range = range.into();
|
||||||
size: f32,
|
let Rangef { min, max } = range;
|
||||||
range: RangeInclusive<f32>,
|
|
||||||
) -> RangeInclusive<f32> {
|
|
||||||
let min = *range.start();
|
|
||||||
let max = *range.end();
|
|
||||||
|
|
||||||
if max - min == f32::INFINITY && size == f32::INFINITY {
|
if max - min == f32::INFINITY && size == f32::INFINITY {
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Min => min..=min + size,
|
Self::Min => Rangef::new(min, min + size),
|
||||||
Self::Center => {
|
Self::Center => {
|
||||||
if size == f32::INFINITY {
|
if size == f32::INFINITY {
|
||||||
f32::NEG_INFINITY..=f32::INFINITY
|
Rangef::new(f32::NEG_INFINITY, f32::INFINITY)
|
||||||
} else {
|
} else {
|
||||||
let left = (min + max) / 2.0 - size / 2.0;
|
let left = (min + max) / 2.0 - size / 2.0;
|
||||||
left..=left + size
|
Rangef::new(left, left + size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Max => max - size..=max,
|
Self::Max => Rangef::new(max - size, max),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,11 +99,12 @@ impl Real for f64 {}
|
|||||||
/// assert_eq!(lerp(1.0..=5.0, 2.0), 9.0);
|
/// assert_eq!(lerp(1.0..=5.0, 2.0), 9.0);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn lerp<R, T>(range: RangeInclusive<R>, t: T) -> R
|
pub fn lerp<R, T>(range: impl Into<RangeInclusive<R>>, t: T) -> R
|
||||||
where
|
where
|
||||||
T: Real + Mul<R, Output = R>,
|
T: Real + Mul<R, Output = R>,
|
||||||
R: Copy + Add<R, Output = R>,
|
R: Copy + Add<R, Output = R>,
|
||||||
{
|
{
|
||||||
|
let range = range.into();
|
||||||
(T::one() - t) * *range.start() + t * *range.end()
|
(T::one() - t) * *range.start() + t * *range.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,20 +139,28 @@ where
|
|||||||
/// Linearly remap a value from one range to another,
|
/// Linearly remap a value from one range to another,
|
||||||
/// so that when `x == from.start()` returns `to.start()`
|
/// so that when `x == from.start()` returns `to.start()`
|
||||||
/// and when `x == from.end()` returns `to.end()`.
|
/// and when `x == from.end()` returns `to.end()`.
|
||||||
pub fn remap<T>(x: T, from: RangeInclusive<T>, to: RangeInclusive<T>) -> T
|
pub fn remap<T>(x: T, from: impl Into<RangeInclusive<T>>, to: impl Into<RangeInclusive<T>>) -> T
|
||||||
where
|
where
|
||||||
T: Real,
|
T: Real,
|
||||||
{
|
{
|
||||||
|
let from = from.into();
|
||||||
|
let to = to.into();
|
||||||
crate::emath_assert!(from.start() != from.end());
|
crate::emath_assert!(from.start() != from.end());
|
||||||
let t = (x - *from.start()) / (*from.end() - *from.start());
|
let t = (x - *from.start()) / (*from.end() - *from.start());
|
||||||
lerp(to, t)
|
lerp(to, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like [`remap`], but also clamps the value so that the returned value is always in the `to` range.
|
/// Like [`remap`], but also clamps the value so that the returned value is always in the `to` range.
|
||||||
pub fn remap_clamp<T>(x: T, from: RangeInclusive<T>, to: RangeInclusive<T>) -> T
|
pub fn remap_clamp<T>(
|
||||||
|
x: T,
|
||||||
|
from: impl Into<RangeInclusive<T>>,
|
||||||
|
to: impl Into<RangeInclusive<T>>,
|
||||||
|
) -> T
|
||||||
where
|
where
|
||||||
T: Real,
|
T: Real,
|
||||||
{
|
{
|
||||||
|
let from = from.into();
|
||||||
|
let to = to.into();
|
||||||
if from.end() < from.start() {
|
if from.end() < from.start() {
|
||||||
return remap_clamp(x, *from.end()..=*from.start(), *to.end()..=*to.start());
|
return remap_clamp(x, *from.end()..=*from.start(), *to.end()..=*to.start());
|
||||||
}
|
}
|
||||||
@@ -174,9 +183,7 @@ where
|
|||||||
/// Round a value to the given number of decimal places.
|
/// Round a value to the given number of decimal places.
|
||||||
pub fn round_to_decimals(value: f64, decimal_places: usize) -> f64 {
|
pub fn round_to_decimals(value: f64, decimal_places: usize) -> f64 {
|
||||||
// This is a stupid way of doing this, but stupid works.
|
// This is a stupid way of doing this, but stupid works.
|
||||||
format!("{:.*}", decimal_places, value)
|
format!("{value:.decimal_places$}").parse().unwrap_or(value)
|
||||||
.parse()
|
|
||||||
.unwrap_or(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_with_minimum_decimals(value: f64, decimals: usize) -> String {
|
pub fn format_with_minimum_decimals(value: f64, decimals: usize) -> String {
|
||||||
@@ -194,7 +201,7 @@ pub fn format_with_decimals_in_range(value: f64, decimal_range: RangeInclusive<u
|
|||||||
if min_decimals != max_decimals {
|
if min_decimals != max_decimals {
|
||||||
// Ugly/slow way of doing this. TODO(emilk): clean up precision.
|
// Ugly/slow way of doing this. TODO(emilk): clean up precision.
|
||||||
for decimals in min_decimals..max_decimals {
|
for decimals in min_decimals..max_decimals {
|
||||||
let text = format!("{:.*}", decimals, value);
|
let text = format!("{value:.decimals$}");
|
||||||
let epsilon = 16.0 * f32::EPSILON; // margin large enough to handle most peoples round-tripping needs
|
let epsilon = 16.0 * f32::EPSILON; // margin large enough to handle most peoples round-tripping needs
|
||||||
if almost_equal(text.parse::<f32>().unwrap(), value as f32, epsilon) {
|
if almost_equal(text.parse::<f32>().unwrap(), value as f32, epsilon) {
|
||||||
// Enough precision to show the value accurately - good!
|
// Enough precision to show the value accurately - good!
|
||||||
@@ -205,7 +212,7 @@ pub fn format_with_decimals_in_range(value: f64, decimal_range: RangeInclusive<u
|
|||||||
// Probably the value was set not by the slider, but from outside.
|
// Probably the value was set not by the slider, but from outside.
|
||||||
// In any case: show the full value
|
// In any case: show the full value
|
||||||
}
|
}
|
||||||
format!("{:.*}", max_decimals, value)
|
format!("{value:.max_decimals$}")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true when arguments are the same within some rounding error.
|
/// Return true when arguments are the same within some rounding error.
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ impl std::ops::Index<usize> for Pos2 {
|
|||||||
match index {
|
match index {
|
||||||
0 => &self.x,
|
0 => &self.x,
|
||||||
1 => &self.y,
|
1 => &self.y,
|
||||||
_ => panic!("Pos2 index out of bounds: {}", index),
|
_ => panic!("Pos2 index out of bounds: {index}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,7 +217,7 @@ impl std::ops::IndexMut<usize> for Pos2 {
|
|||||||
match index {
|
match index {
|
||||||
0 => &mut self.x,
|
0 => &mut self.x,
|
||||||
1 => &mut self.y,
|
1 => &mut self.y,
|
||||||
_ => panic!("Pos2 index out of bounds: {}", index),
|
_ => panic!("Pos2 index out of bounds: {index}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,14 +36,60 @@ impl Rangef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn span(&self) -> f32 {
|
pub fn point(min_and_max: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
min: min_and_max,
|
||||||
|
max: min_and_max,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The length of the range, i.e. `max - min`.
|
||||||
|
#[inline]
|
||||||
|
pub fn span(self) -> f32 {
|
||||||
self.max - self.min
|
self.max - self.min
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn contains(&self, x: f32) -> bool {
|
#[must_use]
|
||||||
|
pub fn contains(self, x: f32) -> bool {
|
||||||
self.min <= x && x <= self.max
|
self.min <= x && x <= self.max
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Equivalent to `x.clamp(min, max)`
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn clamp(self, x: f32) -> f32 {
|
||||||
|
x.clamp(self.min, self.max)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flip `min` and `max` if needed, so that `min <= max` after.
|
||||||
|
#[inline]
|
||||||
|
pub fn as_positive(self) -> Self {
|
||||||
|
Rangef {
|
||||||
|
min: self.min.min(self.max),
|
||||||
|
max: self.min.max(self.max),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shrink by this much on each side, keeping the center
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn shrink(self, amnt: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
min: self.min + amnt,
|
||||||
|
max: self.max - amnt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expand by this much on each side, keeping the center
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn expand(self, amnt: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
min: self.min - amnt,
|
||||||
|
max: self.max + amnt,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Rangef> for RangeInclusive<f32> {
|
impl From<Rangef> for RangeInclusive<f32> {
|
||||||
@@ -108,3 +154,17 @@ impl From<RangeToInclusive<f32>> for Rangef {
|
|||||||
Self::new(f32::NEG_INFINITY, range.end)
|
Self::new(f32::NEG_INFINITY, range.end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq<RangeInclusive<f32>> for Rangef {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &RangeInclusive<f32>) -> bool {
|
||||||
|
self.min == *other.start() && self.max == *other.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<Rangef> for RangeInclusive<f32> {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &Rangef) -> bool {
|
||||||
|
*self.start() == other.min && *self.end() == other.max
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use std::f32::INFINITY;
|
use std::f32::INFINITY;
|
||||||
use std::ops::RangeInclusive;
|
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
@@ -82,15 +81,12 @@ impl Rect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_x_y_ranges(
|
pub fn from_x_y_ranges(x_range: impl Into<Rangef>, y_range: impl Into<Rangef>) -> Self {
|
||||||
x_range: impl Into<RangeInclusive<f32>>,
|
|
||||||
y_range: impl Into<RangeInclusive<f32>>,
|
|
||||||
) -> Self {
|
|
||||||
let x_range = x_range.into();
|
let x_range = x_range.into();
|
||||||
let y_range = y_range.into();
|
let y_range = y_range.into();
|
||||||
Rect {
|
Rect {
|
||||||
min: pos2(*x_range.start(), *y_range.start()),
|
min: pos2(x_range.min, y_range.min),
|
||||||
max: pos2(*x_range.end(), *y_range.end()),
|
max: pos2(x_range.max, y_range.max),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,6 +276,7 @@ impl Rect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `rect.size() == Vec2 { x: rect.width(), y: rect.height() }`
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn size(&self) -> Vec2 {
|
pub fn size(&self) -> Vec2 {
|
||||||
self.max - self.min
|
self.max - self.min
|
||||||
@@ -389,18 +386,18 @@ impl Rect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn x_range(&self) -> RangeInclusive<f32> {
|
pub fn x_range(&self) -> Rangef {
|
||||||
self.min.x..=self.max.x
|
Rangef::new(self.min.x, self.max.x)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn y_range(&self) -> RangeInclusive<f32> {
|
pub fn y_range(&self) -> Rangef {
|
||||||
self.min.y..=self.max.y
|
Rangef::new(self.min.y, self.max.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn bottom_up_range(&self) -> RangeInclusive<f32> {
|
pub fn bottom_up_range(&self) -> Rangef {
|
||||||
self.max.y..=self.min.y
|
Rangef::new(self.max.y, self.min.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `width < 0 || height < 0`
|
/// `width < 0 || height < 0`
|
||||||
|
|||||||
@@ -184,10 +184,7 @@ mod test {
|
|||||||
let expected = vec2(0.0, 3.0);
|
let expected = vec2(0.0, 3.0);
|
||||||
assert!(
|
assert!(
|
||||||
(rotated - expected).length() < 1e-5,
|
(rotated - expected).length() < 1e-5,
|
||||||
"Expected {:?} to equal {:?}. rot: {:?}",
|
"Expected {rotated:?} to equal {expected:?}. rot: {rot:?}",
|
||||||
rotated,
|
|
||||||
expected,
|
|
||||||
rot,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let undone = rot.inverse() * rot;
|
let undone = rot.inverse() * rot;
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ impl std::ops::Index<usize> for Vec2 {
|
|||||||
match index {
|
match index {
|
||||||
0 => &self.x,
|
0 => &self.x,
|
||||||
1 => &self.y,
|
1 => &self.y,
|
||||||
_ => panic!("Vec2 index out of bounds: {}", index),
|
_ => panic!("Vec2 index out of bounds: {index}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,7 +303,7 @@ impl std::ops::IndexMut<usize> for Vec2 {
|
|||||||
match index {
|
match index {
|
||||||
0 => &mut self.x,
|
0 => &mut self.x,
|
||||||
1 => &mut self.y,
|
1 => &mut self.y,
|
||||||
_ => panic!("Vec2 index out of bounds: {}", index),
|
_ => panic!("Vec2 index out of bounds: {index}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# epaint changelog
|
# epaint changelog
|
||||||
All notable changes to the epaint crate will be documented in this file.
|
All notable changes to the epaint crate will be documented in this file.
|
||||||
|
|
||||||
|
This file is updated upon each release.
|
||||||
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
## Unreleased
|
|
||||||
|
|
||||||
|
|
||||||
## 0.22.0 - 2023-05-23
|
## 0.22.0 - 2023-05-23
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ version = "0.22.0"
|
|||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "Minimal 2D graphics library for GUI work"
|
description = "Minimal 2D graphics library for GUI work"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.67"
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/epaint"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/epaint"
|
||||||
license = "(MIT OR Apache-2.0) AND OFL-1.1 AND LicenseRef-UFL-1.0" # OFL and UFL used by default_fonts. See https://github.com/emilk/egui/issues/2321
|
license = "(MIT OR Apache-2.0) AND OFL-1.1 AND LicenseRef-UFL-1.0" # OFL and UFL used by default_fonts. See https://github.com/emilk/egui/issues/2321
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -79,6 +79,7 @@ ahash = { version = "0.8.1", default-features = false, features = [
|
|||||||
"std",
|
"std",
|
||||||
] }
|
] }
|
||||||
nohash-hasher = "0.2"
|
nohash-hasher = "0.2"
|
||||||
|
parking_lot = "0.12" # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios.
|
||||||
|
|
||||||
#! ### Optional dependencies
|
#! ### Optional dependencies
|
||||||
bytemuck = { version = "1.7.2", optional = true, features = ["derive"] }
|
bytemuck = { version = "1.7.2", optional = true, features = ["derive"] }
|
||||||
@@ -94,11 +95,6 @@ serde = { version = "1", optional = true, features = ["derive", "rc"] }
|
|||||||
# native:
|
# native:
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
backtrace = { version = "0.3", optional = true }
|
backtrace = { version = "0.3", optional = true }
|
||||||
parking_lot = "0.12" # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios.
|
|
||||||
|
|
||||||
# web:
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
|
||||||
atomic_refcell = "0.1" # Used instead of parking_lot on on wasm. See https://github.com/emilk/egui/issues/1401
|
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user